<!DOCTYPE html>

<html lang="pl" dir="ltr">

<head>

  <meta charset="UTF-8" />

  <meta name="viewport" content="width=device-width, initial-scale=1.0" />

lunch-pilot/

<link rel="manifest" href="manifest.json">

<meta name="apple-mobile-web-app-capable" content="yes">

<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

<meta name="apple-mobile-web-app-title" content="Lunch Pilot">

<link rel="apple-touch-icon" href="icon-192.png">

<meta name="theme-color" content="#0f172a">

<script>

  if ('serviceWorker' in navigator) {

    window.addEventListener('load', () => {

      navigator.serviceWorker.register('sw.js').catch(() => {});

    });

  }

</script>


  <title>Lunch Pilot B2B Pro — Culinary, Catering & Food Cost OS</title>

  <script src="https://cdn.tailwindcss.com"></script>

  <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=Inter:wght@400;500;600;700;800&family=Playfair+Display:wght@600;700&display=swap" rel="stylesheet" />

  <style>

    body { font-family: 'Inter', sans-serif; }

    .font-serif { font-family: 'Playfair Display', serif; }

    .no-scrollbar::-webkit-scrollbar { display: none; }

    .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }

    @media print {

      body * { visibility: hidden; }

      #printable-menu-area, #printable-menu-area *,

      #printable-event-area, #printable-event-area * { visibility: visible; }

      #printable-menu-area, #printable-event-area {

        position: absolute;

        left: 0;

        top: 0;

        width: 100%;

        box-shadow: none !important;

        border: none !important;

        padding: 12px !important;

      }

    }

  </style>

</head>

<body class="bg-slate-50 text-slate-900 min-h-screen flex flex-col">


  <!-- ======================================================================= -->

  <!-- TOP NAVIGATION BAR                                                      -->

  <!-- ======================================================================= -->

  <header class="bg-slate-900 text-white border-b border-slate-800 sticky top-0 z-40 shadow-lg">

    <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">

      <div class="flex items-center justify-between h-16 gap-3">

        <!-- Brand + Editable Venue Name -->

        <div class="flex items-center gap-3 shrink-0">

          <div class="w-9 h-9 sm:w-10 sm:h-10 rounded-xl bg-gradient-to-br from-emerald-400 to-teal-600 flex items-center justify-center shadow-md shrink-0">

            <svg class="w-5 h-5 sm:w-6 sm:h-6 text-white" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">

              <path stroke-linecap="round" stroke-linejoin="round" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />

            </svg>

          </div>

          <div>

            <div class="flex items-center gap-2">

              <span class="font-extrabold text-base sm:text-lg tracking-tight text-white">Lunch Pilot <span class="text-emerald-400">B2B Pro</span></span>

            </div>

            <div class="flex items-center gap-1.5 mt-0.5">

              <input type="text" id="header-venue-input" oninput="updateVenueNameLive(this.value)" placeholder="Nazwa restauracji..." class="bg-slate-800/90 hover:bg-slate-800 focus:bg-slate-800 text-emerald-300 font-semibold text-xs px-2 py-0.5 rounded-md border border-slate-700 focus:border-emerald-400 outline-none w-36 sm:w-48 transition-colors" />

              <span id="ui-country-badge" class="text-[11px] text-slate-400 hidden md:inline">• PLN (zł)</span>

            </div>

          </div>

        </div>


        <!-- Desktop Navigation Tabs -->

        <nav class="hidden lg:flex items-center gap-1 bg-slate-800/90 p-1 rounded-xl border border-slate-700/70">

          <button onclick="switchTab('generator')" data-tab="generator" class="nav-tab px-3 py-1.5 rounded-lg text-xs font-semibold transition-all bg-emerald-600 text-white shadow-sm cursor-pointer">

            📅 <span data-i18n="tab_menu">Menu (Pn–Pt)</span>

          </button>

          <button onclick="switchTab('ttk')" data-tab="ttk" class="nav-tab px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 hover:text-white hover:bg-slate-700/50 transition-all cursor-pointer">

            📋 <span data-i18n="tab_ttk">Receptury (TTK)</span>

          </button>

          <button onclick="switchTab('procurement')" data-tab="procurement" class="nav-tab px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 hover:text-white hover:bg-slate-700/50 transition-all cursor-pointer">

            📦 <span data-i18n="tab_proc">Lista zakupów</span>

          </button>

          <button onclick="switchTab('events')" data-tab="events" class="nav-tab px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 hover:text-white hover:bg-slate-700/50 transition-all cursor-pointer">

            🎉 <span data-i18n="tab_events">Imprezy / Catering</span>

          </button>

          <button onclick="switchTab('marketing')" data-tab="marketing" class="nav-tab px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 hover:text-white hover:bg-slate-700/50 transition-all cursor-pointer">

            🖨️ <span data-i18n="tab_print">Druk i SMM</span>

          </button>

          <button onclick="switchTab('history')" data-tab="history" class="nav-tab px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 hover:text-white hover:bg-slate-700/50 transition-all cursor-pointer">

            🗂️ <span data-i18n="tab_history">Historia</span>

          </button>

          <button onclick="switchTab('pricing')" data-tab="pricing" class="nav-tab px-3 py-1.5 rounded-lg text-xs font-semibold text-amber-300 hover:text-amber-200 hover:bg-slate-700/50 transition-all cursor-pointer">

            💎 <span data-i18n="tab_pricing">Plany B2B</span>

          </button>

        </nav>


        <!-- Right Controls: 10-Language Selector & Venue Settings -->

        <div class="flex items-center gap-2 shrink-0">

          <select id="lang-selector" onchange="changeLanguage(this.value)" class="bg-slate-800 hover:bg-slate-700 text-white text-xs font-semibold px-2.5 py-2 rounded-xl border border-slate-700 focus:border-emerald-400 outline-none cursor-pointer">

            <option value="pl">🇵🇱 Polski (PL)</option>

            <option value="ru">🇷🇺 Русский (RU)</option>

            <option value="uk">🇺🇦 Українська (UA)</option>

            <option value="it">🇮🇹 Italiano (IT)</option>

            <option value="es">🇪🇸 Español (ES)</option>

            <option value="de">🇩🇪 Deutsch (DE)</option>

            <option value="fr">🇫🇷 Français (FR)</option>

            <option value="tr">🇹🇷 Türkçe (TR)</option>

            <option value="he">🇮🇱 עברית (HE)</option>

            <option value="ar">🇸🇦 العربية (AR)</option>

          </select>


          <button onclick="openSettingsModal()" class="p-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 hover:text-white border border-slate-700 transition-colors cursor-pointer" title="Settings">

            <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">

              <path stroke-linecap="round" stroke-linejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />

              <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />

            </svg>

          </button>

        </div>

      </div>


      <!-- Mobile Tabs Scroller -->

      <div class="flex lg:hidden overflow-x-auto pb-2.5 pt-1 gap-1.5 no-scrollbar">

        <button onclick="switchTab('generator')" data-tab="generator" class="nav-tab shrink-0 px-3 py-1.5 rounded-lg text-xs font-semibold bg-emerald-600 text-white">

          📅 <span data-i18n="tab_menu">Menu (Pn–Pt)</span>

        </button>

        <button onclick="switchTab('ttk')" data-tab="ttk" class="nav-tab shrink-0 px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 bg-slate-800">

          📋 <span data-i18n="tab_ttk">Receptury</span>

        </button>

        <button onclick="switchTab('procurement')" data-tab="procurement" class="nav-tab shrink-0 px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 bg-slate-800">

          📦 <span data-i18n="tab_proc">Zakupy</span>

        </button>

        <button onclick="switchTab('events')" data-tab="events" class="nav-tab shrink-0 px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 bg-slate-800">

          🎉 <span data-i18n="tab_events">Imprezy</span>

        </button>

        <button onclick="switchTab('marketing')" data-tab="marketing" class="nav-tab shrink-0 px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 bg-slate-800">

          🖨️ <span data-i18n="tab_print">Druk/SMM</span>

        </button>

        <button onclick="switchTab('history')" data-tab="history" class="nav-tab shrink-0 px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 bg-slate-800">

          🗂️ <span data-i18n="tab_history">Historia</span>

        </button>

        <button onclick="switchTab('pricing')" data-tab="pricing" class="nav-tab shrink-0 px-3 py-1.5 rounded-lg text-xs font-semibold text-amber-300 bg-slate-800">

          💎 <span data-i18n="tab_pricing">Plany</span>

        </button>

      </div>

    </div>

  </header>


  <!-- ======================================================================= -->

  <!-- MAIN WORKSPACE CONTAINER                                                -->

  <!-- ======================================================================= -->

  <main class="flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-6">


    <!-- Top KPI Summary Strip -->

    <div class="grid grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4 mb-6">

      <div class="bg-white rounded-2xl p-4 border border-slate-200/80 shadow-sm">

        <div class="text-xs font-medium text-slate-500" data-i18n="kpi_meat_fc">Food Cost Zestaw Mięsny 🍖</div>

        <div class="mt-1.5 flex items-baseline justify-between">

          <span id="kpi-avg-fc" class="text-xl sm:text-2xl font-bold text-slate-900">11.40 zł</span>

          <span id="kpi-fc-badge" class="text-[11px] font-semibold px-2 py-0.5 rounded-full bg-emerald-100 text-emerald-700">OK</span>

        </div>

      </div>


      <div class="bg-white rounded-2xl p-4 border border-slate-200/80 shadow-sm">

        <div class="text-xs font-medium text-slate-500" data-i18n="kpi_veg_fc">Food Cost Zestaw Wege 🌱</div>

        <div class="mt-1.5 flex items-baseline justify-between">

          <span id="kpi-veg-fc" class="text-xl sm:text-2xl font-bold text-emerald-700">8.20 zł</span>

          <span class="text-[11px] font-semibold px-2 py-0.5 rounded-full bg-emerald-50 text-emerald-700 border border-emerald-200">VEG</span>

        </div>

      </div>


      <div class="bg-white rounded-2xl p-4 border border-slate-200/80 shadow-sm">

        <div class="text-xs font-medium text-slate-500" data-i18n="kpi_portions">Plan porcji (Pn–Pt)</div>

        <div class="mt-1.5 flex items-baseline justify-between">

          <span id="kpi-total-portions" class="text-xl sm:text-2xl font-bold text-slate-900">270</span>

          <span id="kpi-portions-split" class="text-[11px] text-slate-500">70% 🍖 / 30% 🌱</span>

        </div>

      </div>


      <div class="bg-white rounded-2xl p-4 border border-slate-200/80 shadow-sm">

        <div class="text-xs font-medium text-slate-500" data-i18n="kpi_budget">Budżet zakupów (tydz.)</div>

        <div class="mt-1.5 flex items-baseline justify-between">

          <span id="kpi-weekly-budget" class="text-xl sm:text-2xl font-bold text-slate-900">2 818.00 zł</span>

          <span id="kpi-profit-badge" class="text-[11px] font-semibold px-2 py-0.5 rounded-full bg-emerald-100 text-emerald-800">+7 712 zł</span>

        </div>

      </div>

    </div>


    <!-- ===================================================================== -->

    <!-- VIEW 1: WEEKLY MENU GENERATOR (MON–FRI)                               -->

    <!-- ===================================================================== -->

    <section id="view-generator" class="workspace-view space-y-6">

      <div class="bg-white rounded-2xl border border-slate-200 shadow-sm p-5 sm:p-6">

        <div class="flex flex-col xl:flex-row xl:items-center justify-between gap-4 pb-5 border-b border-slate-100">

          <div>

            <div class="flex flex-wrap items-center gap-2">

              <h1 class="text-lg sm:text-xl font-bold text-slate-900" data-i18n="gen_title">Kreator Lunch Menu (Poniedziałek — Piątek)</h1>

              <span id="cuisine-profile-pill" class="text-xs font-bold px-2.5 py-0.5 rounded-full bg-emerald-50 text-emerald-700 border border-emerald-200">🇵🇱 Kuchnia Polska & Europejska</span>

              <span id="anti-repeat-badge" class="text-xs font-semibold px-2.5 py-0.5 rounded-full bg-indigo-50 text-indigo-700 border border-indigo-200">🛡️ Pamięć historii: 0 zablokowanych dań</span>

              <span id="auto-db-badge" class="text-xs font-semibold px-2.5 py-0.5 rounded-full bg-teal-50 text-teal-800 border border-teal-200">🔄 Baza auto-odnawiana: 280+ dań</span>

            </div>

            <p class="text-xs sm:text-sm text-slate-500 mt-1" data-i18n="gen_subtitle">Kliknij dowolną nazwę dania lub dodatek, aby je poprawić — receptura TTK i lista zakupów przeliczą się automatycznie.</p>

          </div>

          <div class="flex flex-wrap items-center gap-2.5 shrink-0">

            <div class="flex items-center gap-1.5 bg-slate-100 px-3 py-2 rounded-xl border border-slate-200">

              <span class="text-xs font-semibold text-slate-600" data-i18n="lbl_week_start">Poniedziałek:</span>

              <input type="date" id="input-week-start" onchange="savePersistentState()" class="bg-transparent text-xs font-bold text-slate-900 outline-none cursor-pointer" />

            </div>

            <button onclick="autoExpandDishDatabase(true)" class="inline-flex items-center justify-center gap-1.5 px-3.5 py-2.5 rounded-xl bg-teal-600 hover:bg-teal-700 text-white font-semibold text-xs shadow-sm transition-all cursor-pointer" title="Automatycznie dogeneruj nowe sezonowe przepisy do bazy">

              <span>✨</span>

              <span data-i18n="btn_refresh_db">Odnów bazę AI</span>

            </button>

            <button onclick="runFullMenuAudit(true)" class="inline-flex items-center justify-center gap-1.5 px-3.5 py-2.5 rounded-xl bg-indigo-600 hover:bg-indigo-700 text-white font-semibold text-xs shadow-sm transition-all cursor-pointer">

              <span>🔍</span>

              <span data-i18n="btn_verify_menu">Zweryfikuj spójność</span>

            </button>

            <button onclick="saveWeekToHistory()" class="inline-flex items-center justify-center gap-1.5 px-3.5 py-2.5 rounded-xl bg-slate-900 hover:bg-slate-800 text-white font-semibold text-xs shadow-sm transition-all cursor-pointer">

              <span>💾</span>

              <span data-i18n="btn_save_history">Zapisz w historii</span>

            </button>

            <button onclick="generateFullWeek()" id="btn-generate-week" class="inline-flex items-center justify-center gap-2 px-5 py-2.5 rounded-xl bg-emerald-600 hover:bg-emerald-700 active:bg-emerald-800 text-white font-semibold text-xs sm:text-sm shadow-md shadow-emerald-600/20 transition-all cursor-pointer">

              <svg class="w-4 h-4 sm:w-5 sm:h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">

                <path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />

              </svg>

              <span data-i18n="btn_gen_week">Generuj nowy tydzień</span>

            </button>

          </div>

        </div>


        <!-- Kitchen Parameters Grid -->

        <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 pt-5">

          <div>

            <label class="block text-xs font-semibold text-slate-700 mb-1.5" data-i18n="lbl_fc_limit">Limit Food Cost (1 zestaw)</label>

            <div class="relative">

              <input type="number" id="input-target-fc" value="13.50" step="0.50" onchange="recalculateAll()" class="w-full rounded-xl border border-slate-300 px-3.5 py-2.5 text-sm font-semibold text-slate-900 focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 outline-none" />

              <span class="currency-label absolute end-3.5 top-2.5 text-xs font-bold text-slate-400">zł</span>

            </div>

          </div>


          <div>

            <label class="block text-xs font-semibold text-slate-700 mb-1.5" data-i18n="lbl_season">Sezonowość</label>

            <select id="input-season" onchange="onSeasonChange()" class="w-full rounded-xl border border-slate-300 px-3.5 py-2.5 text-sm font-medium text-slate-900 bg-white focus:ring-2 focus:ring-emerald-500 outline-none">

              <option value="autumn" selected data-i18n="season_autumn">🍂 Jesień (dynia, kapusta, grzyby)</option>

              <option value="winter" data-i18n="season_winter">❄️ Zima (sycące zupy, kapusta zasmażana)</option>

              <option value="spring" data-i18n="season_spring">🌱 Wiosna (nowalijki, szparagi, szczaw)</option>

              <option value="summer" data-i18n="season_summer">☀️ Lato (chłodniki, świeże sałatki)</option>

            </select>

          </div>


          <div>

            <label class="block text-xs font-semibold text-slate-700 mb-1.5" data-i18n="lbl_portions">Porcji dziennie (Pn–Cz / Pt)</label>

            <div class="grid grid-cols-2 gap-2">

              <input type="number" id="input-portions-mon-thu" value="50" min="5" onchange="recalculateAll()" class="w-full rounded-xl border border-slate-300 px-3 py-2.5 text-sm font-semibold text-slate-900 focus:ring-2 focus:ring-emerald-500 outline-none" />

              <input type="number" id="input-portions-fri" value="70" min="5" onchange="recalculateAll()" class="w-full rounded-xl border border-slate-300 px-3 py-2.5 text-sm font-semibold text-slate-900 focus:ring-2 focus:ring-emerald-500 outline-none" />

            </div>

          </div>


          <div>

            <label class="block text-xs font-semibold text-slate-700 mb-1.5" data-i18n="lbl_veg_share">Udział dań Wege 🌱</label>

            <div class="flex items-center gap-3 pt-1">

              <input type="range" id="input-veg-share" min="10" max="70" step="5" value="30" oninput="updateVegShareLabel(); recalculateAll()" class="w-full accent-emerald-600 cursor-pointer" />

              <span id="veg-share-label" class="text-xs font-bold text-emerald-700 bg-emerald-50 px-2.5 py-1 rounded-lg border border-emerald-200 shrink-0">30%</span>

            </div>

          </div>

        </div>


        <!-- Zero-Waste Stock Input -->

        <div class="mt-4 pt-4 border-t border-slate-100 flex flex-col sm:flex-row sm:items-center gap-3">

          <div class="flex items-center gap-2 shrink-0">

            <span class="w-2.5 h-2.5 rounded-full bg-amber-500"></span>

            <label class="text-xs font-bold text-slate-700" data-i18n="lbl_stock">Stan magazynu (Zero-Waste):</label>

          </div>

          <input type="text" id="input-stock" oninput="onStockInputChange()" value="" placeholder="np. udko z kurczaka 14 kg, buraki 10 kg, dynia 8 kg..." class="flex-1 rounded-xl border border-slate-300 bg-amber-50/40 px-3.5 py-2 text-xs sm:text-sm text-slate-900 focus:ring-2 focus:ring-amber-500 outline-none" />

          <button onclick="applyStockNow()" class="shrink-0 px-3.5 py-2 rounded-xl bg-amber-500 hover:bg-amber-600 text-slate-950 font-bold text-xs shadow-xs transition-all cursor-pointer" data-i18n="btn_apply_stock">

            ♻️ Użyj stanów w menu

          </button>

        </div>


        <!-- Regular A La Carte Menu Exclusion Input -->

        <div class="mt-3 pt-3 border-t border-slate-100 flex flex-col sm:flex-row sm:items-center gap-3">

          <div class="flex items-center gap-2 shrink-0">

            <span class="w-2.5 h-2.5 rounded-full bg-rose-500"></span>

            <label class="text-xs font-bold text-slate-700" data-i18n="lbl_regular_menu">Stała karta dań (A la carte — nie powtarzać w lunchu):</label>

          </div>

          <input type="text" id="input-regular-menu" oninput="onRegularMenuChange()" placeholder="np. Żurek, Kotlet schabowy, Pierogi ruskie, Dorsz z pieca..." class="flex-1 rounded-xl border border-slate-300 bg-rose-50/30 px-3.5 py-2 text-xs sm:text-sm text-slate-900 focus:ring-2 focus:ring-rose-500 outline-none" />

        </div>

      </div>


      <!-- AI Status Banner -->

      <div id="ai-loading-banner" class="hidden bg-slate-900 text-white rounded-2xl p-4 flex items-center gap-3.5 shadow-lg">

        <div class="w-5 h-5 border-2 border-emerald-400 border-t-transparent rounded-full animate-spin shrink-0"></div>

        <div class="text-xs sm:text-sm font-medium" data-i18n="ai_loading">AI-Chef odnawia bazę receptur i układa menu dopasowane do sezonu oraz magazynu...</div>

      </div>


      <!-- Culinary Pairing Audit Banner -->

      <div id="culinary-audit-banner" class="bg-emerald-50 border border-emerald-200 rounded-2xl p-3.5 sm:p-4 flex flex-col sm:flex-row sm:items-center justify-between gap-2 text-xs sm:text-sm text-emerald-950">

        <div class="flex items-center gap-2.5">

          <span class="text-base">✅</span>

          <span id="culinary-audit-text" class="font-medium">Weryfikacja kulinarna: Wszystkie 5 dni sprawdzone — zero podwójnej skrobi, dodatki i surówki dopasowane smakowo.</span>

        </div>

        <span class="text-[11px] font-bold uppercase tracking-wider px-2.5 py-1 rounded-lg bg-emerald-600 text-white self-start sm:self-auto shrink-0">Chef Audit OK</span>

      </div>


      <!-- 5-DAY CARDS CONTAINER -->

      <div id="weekly-menu-grid" class="space-y-5"></div>

    </section>


    <!-- ===================================================================== -->

    <!-- VIEW 2: TECHNOLOGICAL CARDS (TTK)                                     -->

    <!-- ===================================================================== -->

    <section id="view-ttk" class="workspace-view hidden space-y-6">

      <div class="bg-white rounded-2xl border border-slate-200 shadow-sm p-5 sm:p-6">

        <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">

          <div>

            <h2 class="text-lg sm:text-xl font-bold text-slate-900" data-i18n="ttk_title">Karty Technologiczne (TTK) — Edytowalne Brutto / Netto</h2>

            <p class="text-xs sm:text-sm text-slate-500 mt-0.5" data-i18n="ttk_subtitle">Kliknij dowolny składnik, gramaturę lub cenę, albo dodaj nowy składnik do receptury — koszty przeliczą się od razu.</p>

          </div>

          <div class="inline-flex rounded-xl bg-slate-100 p-1 border border-slate-200 self-start sm:self-auto">

            <button onclick="setTtkMode('single')" id="btn-ttk-single" class="px-3.5 py-1.5 rounded-lg text-xs font-semibold bg-white text-slate-900 shadow-sm cursor-pointer" data-i18n="ttk_mode_single">

              Na 1 porcję (g / ml)

            </button>

            <button onclick="setTtkMode('batch')" id="btn-ttk-batch" class="px-3.5 py-1.5 rounded-lg text-xs font-semibold text-slate-600 hover:text-slate-900 cursor-pointer" data-i18n="ttk_mode_batch">

              Na partię dnia (kg / l)

            </button>

          </div>

        </div>

        <div id="ttk-day-pills" class="flex flex-wrap gap-2 mt-5 pt-4 border-t border-slate-100"></div>

      </div>

      <div id="ttk-cards-container" class="grid grid-cols-1 lg:grid-cols-2 gap-5"></div>

    </section>


    <!-- ===================================================================== -->

    <!-- VIEW 3: PROCUREMENT & SUPPLIER ORDER                                  -->

    <!-- ===================================================================== -->

    <section id="view-procurement" class="workspace-view hidden space-y-6">

      <div class="bg-white rounded-2xl border border-slate-200 shadow-sm p-5 sm:p-6">

        <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">

          <div>

            <h2 class="text-lg sm:text-xl font-bold text-slate-900" data-i18n="proc_title">Zbiorcza lista zakupów na tydzień</h2>

            <p class="text-xs sm:text-sm text-slate-500 mt-0.5" data-i18n="proc_subtitle">Uwzględnia mięso, warzywa, oleje, płyny i przyprawy w podziale na 2 dostawy.</p>

          </div>

          <div class="flex flex-wrap items-center gap-2">

            <div class="inline-flex rounded-xl bg-slate-100 p-1 border border-slate-200">

              <button onclick="setProcurementSplit('all')" id="btn-proc-all" class="px-3 py-1.5 rounded-lg text-xs font-semibold bg-white text-slate-900 shadow-sm cursor-pointer" data-i18n="proc_all">Cały tydzień</button>

              <button onclick="setProcurementSplit('wave1')" id="btn-proc-wave1" class="px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-600 cursor-pointer" data-i18n="proc_w1">Dostawa 1 (Pn–Wt)</button>

              <button onclick="setProcurementSplit('wave2')" id="btn-proc-wave2" class="px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-600 cursor-pointer" data-i18n="proc_w2">Dostawa 2 (Śr–Pt)</button>

            </div>

            <button onclick="copyProcurementList()" class="inline-flex items-center gap-1.5 px-4 py-2 rounded-xl bg-slate-900 hover:bg-slate-800 text-white text-xs font-semibold cursor-pointer">

              📋 <span data-i18n="btn_copy_proc">Kopiuj dla dostawcy</span>

            </button>

          </div>

        </div>

      </div>

      <div id="procurement-categories-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5"></div>

    </section>


    <!-- ===================================================================== -->

    <!-- VIEW 4: EVENTS & BANQUET CATERING ORGANIZER                           -->

    <!-- ===================================================================== -->

    <section id="view-events" class="workspace-view hidden space-y-6">

      <div class="bg-white rounded-2xl border border-slate-200 shadow-sm p-5 sm:p-6">

        <div class="flex flex-col lg:flex-row lg:items-center justify-between gap-4 pb-5 border-b border-slate-100">

          <div>

            <div class="flex items-center gap-2">

              <h2 class="text-lg sm:text-xl font-bold text-slate-900" data-i18n="ev_title">Organizator Imprez, Bankietów i Cateringu B2B</h2>

              <span class="text-xs font-bold px-2.5 py-0.5 rounded-full bg-purple-100 text-purple-800">Event & Banquet OS</span>

            </div>

            <p class="text-xs sm:text-sm text-slate-500 mt-1" data-i18n="ev_subtitle">Szybka kalkulacja przyjęcia (chrzciny, komunia, konferencja, wigilia firmowa): koszt surowca, marża, oferta PDF dla klienta i lista zakupów.</p>

          </div>

          <div class="flex flex-wrap items-center gap-2">

            <button onclick="importFromLunchToEvent()" class="px-3.5 py-2.5 rounded-xl bg-slate-100 hover:bg-slate-200 text-slate-900 font-semibold text-xs cursor-pointer">

              📥 <span data-i18n="ev_btn_import">Pobierz hity z lunchu</span>

            </button>

            <button onclick="generateEventProposalAi()" class="px-3.5 py-2.5 rounded-xl bg-purple-600 hover:bg-purple-700 text-white font-semibold text-xs shadow-sm cursor-pointer">

              ✨ <span data-i18n="ev_btn_ai">Ułóż menu na imprezę</span>

            </button>

            <button onclick="window.print()" class="px-3.5 py-2.5 rounded-xl bg-emerald-600 hover:bg-emerald-700 text-white font-semibold text-xs shadow-sm cursor-pointer">

              🖨️ <span data-i18n="ev_btn_print">Drukuj ofertę</span>

            </button>

          </div>

        </div>


        <!-- Event Parameters -->

        <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4 pt-5">

          <div>

            <label class="block text-xs font-semibold text-slate-700 mb-1" data-i18n="ev_lbl_name">Nazwa imprezy / Klient</label>

            <input type="text" id="ev-name" value="Bankiet Firmowy — Sala Główna" oninput="renderEventView()" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-xs sm:text-sm font-semibold text-slate-900" />

          </div>

          <div>

            <label class="block text-xs font-semibold text-slate-700 mb-1" data-i18n="ev_lbl_date">Data i godzina</label>

            <input type="date" id="ev-date" onchange="renderEventView()" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-xs sm:text-sm font-semibold text-slate-900" />

          </div>

          <div>

            <label class="block text-xs font-semibold text-slate-700 mb-1" data-i18n="ev_lbl_guests">Liczba gości (os.)</label>

            <input type="number" id="ev-guests" value="45" min="5" onchange="renderEventView()" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-xs sm:text-sm font-bold text-slate-900" />

          </div>

          <div>

            <label class="block text-xs font-semibold text-slate-700 mb-1" data-i18n="ev_lbl_price_guest">Cena za osobę (oferta)</label>

            <div class="relative">

              <input type="number" id="ev-price-per-guest" value="145" min="10" onchange="renderEventView()" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-xs sm:text-sm font-bold text-slate-900" />

              <span class="currency-label absolute end-3 top-2 text-xs font-bold text-slate-400">zł</span>

            </div>

          </div>

          <div>

            <label class="block text-xs font-semibold text-slate-700 mb-1" data-i18n="ev_lbl_type">Typ wydarzenia</label>

            <select id="ev-type" onchange="renderEventView()" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-xs sm:text-sm font-medium bg-white">

              <option value="corporate">🏢 Konferencja / Lunch firmowy</option>

              <option value="family">🥂 Chrzciny / Komunia / Urodziny</option>

              <option value="buffet">🍽️ Bufet szwedzki / Catering wyjazdowy</option>

            </select>

          </div>

        </div>

      </div>


      <!-- Event Financial KPIs -->

      <div class="grid grid-cols-2 lg:grid-cols-4 gap-4" id="event-kpi-strip"></div>


      <!-- Event Menu Builder + Printable Offer -->

      <div class="grid grid-cols-1 lg:grid-cols-12 gap-6">

        <div class="lg:col-span-6 bg-white rounded-2xl border border-slate-200 p-5 shadow-sm space-y-4">

          <div class="flex items-center justify-between border-b border-slate-100 pb-3">

            <h3 class="text-sm font-bold text-slate-900" data-i18n="ev_menu_editor">Pozycje menu na imprezę (kliknij, aby edytować)</h3>

            <button onclick="addEventCourse()" class="px-3 py-1.5 rounded-lg bg-slate-900 text-white text-xs font-semibold cursor-pointer">+ Dodaj pozycję</button>

          </div>

          <div id="event-courses-list" class="space-y-3"></div>

        </div>


        <div id="printable-event-area" class="lg:col-span-6 bg-white rounded-2xl border border-slate-300 p-6 sm:p-8 shadow-md space-y-5">

          <div class="text-center border-b-2 border-slate-900 pb-4">

            <div class="text-xs uppercase tracking-[0.2em] text-slate-500 font-bold" id="ev-print-venue">RESTAURACJA STUDIO BUFFO</div>

            <h3 class="font-serif text-2xl font-bold text-slate-900 mt-1" id="ev-print-title">OFERTA PRZYJĘCIA / BANKIETU</h3>

            <div class="mt-2 text-xs text-slate-600" id="ev-print-meta">45 gości • 145 zł / os.</div>

          </div>

          <div id="ev-print-courses" class="space-y-3 text-sm"></div>

          <div class="pt-4 border-t border-slate-200 flex items-center justify-between text-xs text-slate-600">

            <span id="ev-print-allergens">Alergeny: Gluten, Laktoza, Seler</span>

            <span class="font-bold text-slate-900 text-sm" id="ev-print-total">Razem: 6 525.00 zł</span>

          </div>

        </div>

      </div>

    </section>


    <!-- ===================================================================== -->

    <!-- VIEW 5: PRINT MENU (1 DAY OR FULL WEEK) & SOCIAL MEDIA                -->

    <!-- ===================================================================== -->

    <section id="view-marketing" class="workspace-view hidden space-y-6">

      <div class="grid grid-cols-1 lg:grid-cols-12 gap-6">

        <div class="lg:col-span-7 space-y-4">

          <div class="bg-white rounded-2xl border border-slate-200 p-4 space-y-3 shadow-sm">

            <div class="flex flex-wrap items-center justify-between gap-3">

              <div>

                <h3 class="text-sm font-bold text-slate-900" data-i18n="print_card_title">Projekt menu do druku (A4 / A5)</h3>

                <p class="text-xs text-slate-500" data-i18n="print_card_sub">Wybierz wydruk na 1 konkretny dzień lub cały tydzień</p>

              </div>

              <div class="flex items-center gap-2">

                <label class="text-xs font-semibold text-slate-600" data-i18n="lbl_guest_price">Cena:</label>

                <input type="number" id="input-guest-price" value="39" onchange="recalculateAll()" class="w-16 rounded-lg border border-slate-300 px-2 py-1 text-xs font-bold text-slate-900" />

                <span class="currency-label text-xs font-bold text-slate-500">zł</span>

                <button onclick="window.print()" class="px-3.5 py-2 rounded-xl bg-emerald-600 hover:bg-emerald-700 text-white text-xs font-semibold cursor-pointer">

                  🖨️ <span data-i18n="btn_print">Drukuj</span>

                </button>

              </div>

            </div>


            <!-- Print Mode Switcher: 1 Day vs Full Week -->

            <div class="pt-3 border-t border-slate-100 flex flex-wrap items-center justify-between gap-2">

              <div class="inline-flex rounded-xl bg-slate-100 p-1 border border-slate-200">

                <button onclick="setPrintScope('week')" id="btn-print-week" class="px-3 py-1.5 rounded-lg text-xs font-semibold bg-white text-slate-900 shadow-sm cursor-pointer" data-i18n="print_scope_week">

                  📅 Cały tydzień (Pn–Pt)

                </button>

                <button onclick="setPrintScope('day')" id="btn-print-day" class="px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-600 cursor-pointer" data-i18n="print_scope_day">

                  📄 Tylko 1 dzień

                </button>

              </div>

              <div id="print-day-selector-wrap" class="hidden items-center gap-2">

                <select id="print-day-select" onchange="setPrintDayIndex(parseInt(this.value))" class="rounded-xl border border-slate-300 bg-white px-3 py-1.5 text-xs font-bold text-slate-900 outline-none cursor-pointer"></select>

              </div>

            </div>

          </div>


          <!-- Printable Sheet -->

          <div id="printable-menu-area" class="bg-white rounded-2xl border border-slate-300 shadow-md p-6 sm:p-10 text-slate-900">

            <div class="text-center border-b-2 border-slate-900 pb-5 mb-6">

              <div class="text-xs sm:text-sm uppercase tracking-[0.25em] text-slate-600 font-bold mb-1" id="print-venue-title">RESTAURACJA STUDIO BUFFO</div>

              <h2 class="font-serif text-2xl sm:text-4xl font-bold tracking-tight text-slate-900" id="print-main-heading" data-i18n="print_menu_header">LUNCH MENU TYGODNIA</h2>

              <div class="mt-2.5 inline-flex flex-wrap justify-center items-center gap-2 text-xs font-medium text-slate-700 bg-slate-100 px-4 py-1.5 rounded-full">

                <span id="print-subtitle-range">Poniedziałek — Piątek</span>

                <span>•</span>

                <span>12:00 – 16:00</span>

                <span>•</span>

                <span class="font-bold text-slate-900" id="print-combo-price">39 zł</span>

              </div>

            </div>

            <div id="printable-days-list" class="space-y-5"></div>

          </div>

        </div>


        <!-- SMM Post Box -->

        <div class="lg:col-span-5 space-y-4">

          <div class="bg-white rounded-2xl border border-slate-200 p-5 shadow-sm space-y-4">

            <div class="flex items-center justify-between">

              <div>

                <h3 class="text-sm font-bold text-slate-900" data-i18n="smm_title">Post na Instagram / Telegram</h3>

                <p class="text-xs text-slate-500" data-i18n="smm_sub">Gotowy tekst w wybranym języku z ikonami 🌱</p>

              </div>

              <button onclick="copySmmPost()" class="px-3.5 py-2 rounded-xl bg-slate-900 hover:bg-slate-800 text-white text-xs font-semibold cursor-pointer">

                📋 <span data-i18n="btn_copy_smm">Kopiuj</span>

              </button>

            </div>

            <textarea id="smm-post-textarea" rows="19" class="w-full rounded-xl border border-slate-300 bg-slate-50 p-3.5 text-xs sm:text-sm text-slate-800 leading-relaxed focus:ring-2 focus:ring-emerald-500 outline-none"></textarea>

          </div>

        </div>

      </div>

    </section>


    <!-- ===================================================================== -->

    <!-- VIEW 6: MENU HISTORY LOG & ANTI-REPETITION MEMORY                     -->

    <!-- ===================================================================== -->

    <section id="view-history" class="workspace-view hidden space-y-6">

      <div class="bg-white rounded-2xl border border-slate-200 shadow-sm p-5 sm:p-6 flex flex-col sm:flex-row sm:items-center justify-between gap-4">

        <div>

          <h2 class="text-lg sm:text-xl font-bold text-slate-900" data-i18n="hist_title">Historia wydanych lunchy i pamięć anty-powtórek</h2>

          <p class="text-xs sm:text-sm text-slate-500 mt-0.5" data-i18n="hist_subtitle">Zapisane dni i tygodnie są pamiętane w przeglądarce — generator automatycznie omija te dania przez 30 dni.</p>

        </div>

        <button onclick="clearHistoryLog()" class="self-start sm:self-auto px-4 py-2.5 rounded-xl bg-rose-50 hover:bg-rose-100 text-rose-700 border border-rose-200 text-xs font-semibold cursor-pointer" data-i18n="btn_clear_hist">

          🗑️ Wyczyść całą historię

        </button>

      </div>


      <div id="history-log-container" class="space-y-3"></div>

    </section>


    <!-- ===================================================================== -->

    <!-- VIEW 7: B2B SAAS PRICING                                              -->

    <!-- ===================================================================== -->

    <section id="view-pricing" class="workspace-view hidden space-y-6">

      <div class="bg-slate-900 text-white rounded-3xl p-6 sm:p-10 text-center max-w-4xl mx-auto shadow-xl">

        <span class="inline-block px-3 py-1 rounded-full bg-emerald-500/20 text-emerald-300 text-xs font-bold mb-3">Lunch Pilot B2B Pro • SaaS</span>

        <h2 class="text-2xl sm:text-3xl font-extrabold tracking-tight" data-i18n="pricing_hero_title">Zwraca się od pierwszego uratowanego kilograma produktów</h2>

        <p class="text-slate-300 text-sm sm:text-base mt-2 max-w-2xl mx-auto" data-i18n="pricing_hero_sub">Oszczędza szefowi kuchni 4 godziny tygodniowo na układaniu menu lokalnego, receptur z przyprawami i zamówień.</p>

      </div>


      <div class="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-6xl mx-auto">

        <div class="bg-white rounded-2xl border border-slate-200 p-6 flex flex-col justify-between shadow-sm">

          <div>

            <div class="text-sm font-bold text-slate-500" data-i18n="plan1_name">Start (Kawiarnia / Bistro)</div>

            <div class="mt-3 flex items-baseline gap-1">

              <span class="text-3xl font-extrabold text-slate-900">79 zł</span>

              <span class="text-xs text-slate-500" data-i18n="per_month">/ mies.</span>

            </div>

            <ul class="mt-6 space-y-3 text-xs sm:text-sm text-slate-700">

              <li data-i18n="feat_1">✓ 10 języków + adaptacja kuchni pod kraj</li>

              <li data-i18n="feat_2">✓ 2 zupy + 2 dania główne (🍖 + 🌱)</li>

              <li data-i18n="feat_3">✓ Wydruk na 1 dzień / cały tydzień + SMM</li>

            </ul>

          </div>

          <button onclick="showToast('Demo: Plan Start (14 dni gratis)')" class="mt-8 w-full py-2.5 rounded-xl bg-slate-100 hover:bg-slate-200 text-slate-900 font-semibold text-xs sm:text-sm cursor-pointer" data-i18n="btn_try_free">14 dni za darmo</button>

        </div>


        <div class="bg-slate-900 text-white rounded-2xl border-2 border-emerald-500 p-6 flex flex-col justify-between shadow-xl relative">

          <span class="absolute -top-3 end-5 bg-emerald-500 text-slate-950 text-[10px] font-extrabold uppercase px-2.5 py-0.5 rounded-full">Bestseller</span>

          <div>

            <div class="text-sm font-bold text-emerald-400" data-i18n="plan2_name">Szef-Pro (Pełny cykl)</div>

            <div class="mt-3 flex items-baseline gap-1">

              <span class="text-3xl font-extrabold text-white">149 zł</span>

              <span class="text-xs text-slate-400" data-i18n="per_month">/ mies.</span>

            </div>

            <ul class="mt-6 space-y-3 text-xs sm:text-sm text-slate-200">

              <li data-i18n="feat_4">✓ Pełne TTK (woda, tłuszcze, przyprawy)</li>

              <li data-i18n="feat_5">✓ Zero-Waste: zarządzanie stanem magazynu</li>

              <li data-i18n="feat_6">✓ Organizator imprez i cateringu B2B</li>

            </ul>

          </div>

          <button onclick="showToast('Demo: Plan Szef-Pro (14 dni gratis)')" class="mt-8 w-full py-2.5 rounded-xl bg-emerald-500 hover:bg-emerald-400 text-slate-950 font-bold text-xs sm:text-sm cursor-pointer" data-i18n="btn_try_free">14 dni za darmo</button>

        </div>


        <div class="bg-white rounded-2xl border border-slate-200 p-6 flex flex-col justify-between shadow-sm">

          <div>

            <div class="text-sm font-bold text-slate-500" data-i18n="plan3_name">Sieć / Catering</div>

            <div class="mt-3 flex items-baseline gap-1">

              <span class="text-3xl font-extrabold text-slate-900">349 zł</span>

              <span class="text-xs text-slate-500" data-i18n="per_month">/ mies.</span>

            </div>

            <ul class="mt-6 space-y-3 text-xs sm:text-sm text-slate-700">

              <li data-i18n="feat_7">✓ Do 5 lokali w jednym panelu</li>

              <li data-i18n="feat_8">✓ Zbiorcze zamówienia na magazyn centralny</li>

              <li data-i18n="feat_9">✓ Eksport do iiko / Poster / R-Keeper</li>

            </ul>

          </div>

          <button onclick="showToast('Demo: Kontakt B2B')" class="mt-8 w-full py-2.5 rounded-xl bg-slate-900 hover:bg-slate-800 text-white font-semibold text-xs sm:text-sm cursor-pointer" data-i18n="btn_contact">Zamów demo</button>

        </div>

      </div>

    </section>


  </main>


  <!-- ======================================================================= -->

  <!-- MODALS: SETTINGS, DISH EDITOR, INGREDIENT EDITOR                        -->

  <!-- ======================================================================= -->

  <div id="settings-modal" class="hidden fixed inset-0 z-50 bg-slate-950/60 backdrop-blur-xs flex items-center justify-center p-4">

    <div class="bg-white rounded-2xl max-w-md w-full p-6 shadow-2xl border border-slate-200 space-y-4">

      <div class="flex items-center justify-between border-b border-slate-100 pb-3">

        <h3 class="text-base font-bold text-slate-900" data-i18n="settings_title">Ustawienia lokalu</h3>

        <button onclick="closeSettingsModal()" class="text-slate-400 hover:text-slate-700 text-lg font-bold cursor-pointer">✕</button>

      </div>

      <div>

        <label class="block text-xs font-semibold text-slate-700 mb-1" data-i18n="lbl_venue_name">Nazwa lokalu</label>

        <input type="text" id="setting-venue-name" value="Restauracja Studio Buffo" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm" />

      </div>

      <div>

        <label class="block text-xs font-semibold text-slate-700 mb-1" data-i18n="lbl_currency">Waluta</label>

        <select id="setting-currency" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm bg-white">

          <option value="zł">PLN (zł)</option>

          <option value="€">EUR (€)</option>

          <option value="$">USD ($)</option>

          <option value="₴">UAH (₴)</option>

          <option value="₽">RUB (₽)</option>

          <option value="₺">TRY (₺)</option>

          <option value="₪">ILS (₪)</option>

          <option value="AED">AED</option>

        </select>

      </div>

      <div class="pt-2 flex justify-end gap-2">

        <button onclick="saveSettings()" class="px-4 py-2 rounded-xl bg-emerald-600 hover:bg-emerald-700 text-white text-xs font-semibold cursor-pointer" data-i18n="btn_save">Zapisz</button>

      </div>

    </div>

  </div>


  <!-- Dish & Sides Quick Editor Modal (Chef Interactive Adjustment) -->

  <div id="dish-edit-modal" class="hidden fixed inset-0 z-50 bg-slate-950/60 backdrop-blur-xs flex items-center justify-center p-4">

    <div class="bg-white rounded-2xl max-w-lg w-full p-5 sm:p-6 shadow-2xl border border-slate-200 space-y-4">

      <div class="flex items-center justify-between border-b border-slate-100 pb-3">

        <h3 class="text-sm sm:text-base font-bold text-slate-900">✏️ Koryguj danie i dodatki (Auto-przeliczenie TTK)</h3>

        <button onclick="closeDishEditModal()" class="text-slate-400 hover:text-slate-700 text-base font-bold cursor-pointer">✕</button>

      </div>

      <div>

        <label class="block text-xs font-semibold text-slate-700 mb-1">Nazwa dania / zupy:</label>

        <input type="text" id="dish-edit-title" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-xs sm:text-sm font-bold text-slate-900 focus:ring-2 focus:ring-emerald-500 outline-none" />

      </div>

      <div id="dish-edit-sides-wrap" class="space-y-3">

        <div>

          <label class="block text-xs font-semibold text-slate-700 mb-1">Dodatek skrobiowy / wykończenie (wybierz lub wpisz własny):</label>

          <input type="text" id="dish-edit-side" list="starch-suggestions" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-xs sm:text-sm text-slate-900 focus:ring-2 focus:ring-emerald-500 outline-none" />

          <datalist id="starch-suggestions"></datalist>

        </div>

        <div>

          <label class="block text-xs font-semibold text-slate-700 mb-1">Surówka / warzywa na ciepło:</label>

          <input type="text" id="dish-edit-salad" list="salad-suggestions" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-xs sm:text-sm text-slate-900 focus:ring-2 focus:ring-emerald-500 outline-none" />

          <datalist id="salad-suggestions"></datalist>

        </div>

      </div>

      <div>

        <label class="block text-xs font-semibold text-slate-700 mb-1">Technologia / Temperatura i czas obróbki:</label>

        <textarea id="dish-edit-tech" rows="2" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-xs text-slate-800 focus:ring-2 focus:ring-emerald-500 outline-none"></textarea>

      </div>

      <div class="pt-2 flex justify-between items-center gap-2">

        <button onclick="openTtkFromDishModal()" class="px-3.5 py-2 rounded-xl bg-slate-100 hover:bg-slate-200 text-slate-800 text-xs font-semibold cursor-pointer">📋 Edytuj składniki w TTK</button>

        <div class="flex gap-2">

          <button onclick="closeDishEditModal()" class="px-3.5 py-2 rounded-xl bg-slate-100 text-slate-700 text-xs font-semibold cursor-pointer">Anuluj</button>

          <button onclick="confirmDishEdit()" class="px-4 py-2 rounded-xl bg-emerald-600 hover:bg-emerald-700 text-white text-xs font-semibold cursor-pointer">Zapisz i przelicz</button>

        </div>

      </div>

    </div>

  </div>


  <!-- Ingredient & Recipe Row Editor Modal -->

  <div id="price-edit-modal" class="hidden fixed inset-0 z-50 bg-slate-950/60 backdrop-blur-xs flex items-center justify-center p-4">

    <div class="bg-white rounded-2xl max-w-sm w-full p-5 shadow-2xl border border-slate-200 space-y-4">

      <div class="flex items-center justify-between border-b border-slate-100 pb-2.5">

        <h3 class="text-sm font-bold text-slate-900" id="price-edit-title">Edycja składnika w TTK</h3>

        <button onclick="closePriceEditModal()" class="text-slate-400 hover:text-slate-700 text-base font-bold cursor-pointer">✕</button>

      </div>

      <div class="space-y-3">

        <div>

          <label class="block text-xs font-semibold text-slate-600 mb-1">Nazwa surowca:</label>

          <input type="text" id="ing-edit-name" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-xs sm:text-sm font-semibold text-slate-900" />

        </div>

        <div class="grid grid-cols-2 gap-2">

          <div>

            <label class="block text-xs font-semibold text-slate-600 mb-1">Gramatura Brutto (g/ml):</label>

            <input type="number" step="1" min="1" id="ing-edit-gross" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm font-bold text-slate-900" />

          </div>

          <div>

            <label class="block text-xs font-semibold text-slate-600 mb-1">Odpad (%):</label>

            <input type="number" step="1" min="0" max="80" id="ing-edit-waste" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm font-bold text-slate-900" />

          </div>

        </div>

        <div>

          <label class="block text-xs font-semibold text-slate-600 mb-1" id="price-edit-label">Cena za 1 kg / 1 l:</label>

          <input type="number" step="0.01" min="0" id="price-edit-input" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm font-bold text-slate-900 focus:ring-2 focus:ring-emerald-500 outline-none" />

        </div>

      </div>

      <div class="flex justify-between items-center gap-2 pt-1">

        <button onclick="deleteIngredientFromTtk()" id="btn-delete-ing" class="px-3 py-2 rounded-xl bg-rose-50 hover:bg-rose-100 text-rose-700 text-xs font-semibold cursor-pointer">🗑️ Usuń</button>

        <div class="flex gap-2">

          <button onclick="closePriceEditModal()" class="px-3.5 py-2 rounded-xl bg-slate-100 text-slate-700 text-xs font-semibold cursor-pointer">✕</button>

          <button onclick="confirmIngredientPriceEdit()" class="px-4 py-2 rounded-xl bg-emerald-600 hover:bg-emerald-700 text-white text-xs font-semibold cursor-pointer" data-i18n="btn_save">Zapisz</button>

        </div>

      </div>

    </div>

  </div>


  <!-- Toast Notification -->

  <div id="toast-notification" class="hidden fixed bottom-5 end-5 z-50 bg-slate-900 text-white text-xs sm:text-sm font-medium px-4 py-3 rounded-xl shadow-xl border border-slate-700"></div>


  <script>

    // =========================================================================

    // 1. COMPLETE 10-LANGUAGE UI & INGREDIENT LOCALIZATION DICTIONARY

    // =========================================================================

    const STORAGE_KEY = "lunch_pilot_b2b_pro_v4";


    const I18N = {

      pl: {

        dir: "ltr", countryBadge: "• PLN (zł)", currency: "zł", defaultFc: 13.50, defaultGuestPrice: 39,

        cuisinePill: "🇵🇱 Kuchnia Polska & Europejska",

        defaultStock: "",

        days: ["Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek"],

        short_mon_thu: "Pn–Cz", short_fri: "Pt", portions_unit: "porcji",

        tab_menu: "Menu (Pn–Pt)", tab_ttk: "Receptury (TTK)", tab_proc: "Lista zakupów", tab_events: "Imprezy / Catering", tab_print: "Druk i SMM", tab_history: "Historia", tab_pricing: "Plany B2B",

        lbl_week_start: "Poniedziałek:", btn_refresh_db: "Odnów bazę AI", btn_verify_menu: "Zweryfikuj spójność", btn_save_history: "Zapisz w historii", btn_gen_week: "Generuj nowy tydzień",

        anti_repeat_tpl: "🛡️ Pamięć historii: zablokowano {n} dań z ostatnich 30 dni",

        audit_ok_tpl: "Weryfikacja kulinarna: Wszystkie 5 dni sprawdzone — brak podwójnej skrobi, smaki i dodatki 100% spójne.",

        audit_fixed_tpl: "Weryfikacja kulinarna: Automatycznie skorygowano {n} niepasujących połączeń i przeliczono TTK!",

        comp_topping: "Wykończenie",

        hist_title: "Historia wydanych lunchy i pamięć anty-powtórek",

        hist_subtitle: "Zapisane dni i tygodnie są pamiętane w przeglądarce — generator automatycznie omija te dania przez 30 dni.",

        btn_clear_hist: "🗑️ Wyczyść całą historię",

        hist_empty: "Brak zapisanych menu w historii. Ustaw datę poniedziałku i kliknij „Zapisz w historii” w zakładce Menu.",

        hist_saved_toast: "Zapisano tydzień w historii! Dania zablokowane przed powtórką na 30 dni.",

        lbl_fc_limit: "Limit Food Cost (1 zestaw)", lbl_season: "Sezonowość", lbl_portions: "Porcji dziennie (Pn–Cz / Pt)", lbl_veg_share: "Udział dań Wege 🌱", lbl_stock: "Stan magazynu (Zero-Waste):",

        btn_apply_stock: "♻️ Użyj stanów w menu",

        lbl_regular_menu: "Stała karta dań (A la carte — nie powtarzać w lunchu):",

        season_autumn: "🍂 Jesień (dynia, kapusta, grzyby)", season_winter: "❄️ Zima (sycące zupy, kapusta zasmażana)", season_spring: "🌱 Wiosna (nowalijki, szparagi, szczaw)", season_summer: "☀️ Lato (chłodniki, świeże sałatki)",

        sec_soups: "1. Pierwsze danie (Zupy)", sec_mains: "2. Drugie danie",

        badge_meat_soup: "🍖 Zupa na mięsie", badge_veg_soup: "🌱 Zupa wegetariańska",

        badge_meat_main: "🍖 Danie mięsne", badge_veg_main: "🌱 Danie wegetariańskie",

        comp_side: "Dodatek", btn_swap: "Zmień", btn_swap_salad: "Zmień surówkę / warzywa", btn_ttk: "TTK",

        ttk_title: "Karty Technologiczne (TTK) — Edytowalne Brutto / Netto", ttk_subtitle: "Kliknij dowolny składnik, gramaturę lub cenę, albo dodaj nowy składnik do receptury — koszty przeliczą się od razu.",

        ttk_mode_single: "Na 1 porcję (g / ml)", ttk_mode_batch: "Na partię dnia (kg / l)",

        proc_title: "Zbiorcza lista zakupów na tydzień", proc_subtitle: "Uwzględnia mięso, warzywa, oleje, płyny i przyprawy w podziale na 2 dostawy.",

        proc_all: "Cały tydzień", proc_w1: "Dostawa 1 (Pn–Wt)", proc_w2: "Dostawa 2 (Śr–Pt)", btn_copy_proc: "Kopiuj dla dostawcy",

        ev_title: "Organizator Imprez, Bankietów i Cateringu B2B", ev_subtitle: "Szybka kalkulacja przyjęcia (chrzciny, komunia, konferencja, wigilia firmowa): koszt surowca, marża, oferta PDF dla klienta i lista zakupów.",

        ev_btn_import: "Pobierz hity z lunchu", ev_btn_ai: "Ułóż menu na imprezę", ev_btn_print: "Drukuj ofertę",

        ev_lbl_name: "Nazwa imprezy / Klient", ev_lbl_date: "Data i godzina", ev_lbl_guests: "Liczba gości (os.)", ev_lbl_price_guest: "Cena za osobę (oferta)", ev_lbl_type: "Typ wydarzenia",

        ev_menu_editor: "Pozycje menu na imprezę (kliknij, aby edytować)",

        print_card_title: "Projekt menu do druku (A4 / A5)", print_card_sub: "Wybierz wydruk na 1 konkretny dzień lub cały tydzień", lbl_guest_price: "Cena:", btn_print: "Drukuj",

        print_scope_week: "📅 Cały tydzień (Pn–Pt)", print_scope_day: "📄 Tylko 1 dzień",

        print_menu_header: "LUNCH MENU TYGODNIA", print_day_header: "LUNCH DNIA", print_days_range: "Poniedziałek — Piątek",

        smm_title: "Post na Instagram / Telegram", smm_sub: "Gotowy tekst w wybranym języku z ikonami 🌱", btn_copy_smm: "Kopiuj",

        pricing_hero_title: "Zwraca się od pierwszego uratowanego kilograma produktów",

        pricing_hero_sub: "Oszczędza szefowi kuchni 4 godziny tygodniowo na układaniu menu lokalnego, receptur z przyprawami i zamówień.",

        plan1_name: "Start (Kawiarnia / Bistro)", plan2_name: "Szef-Pro (Pełny cykl)", plan3_name: "Sieć / Catering", per_month: "/ mies.",

        feat_1: "✓ 10 języków + adaptacja kuchni pod kraj", feat_2: "✓ 2 zupy + 2 dania główne (🍖 + 🌱)", feat_3: "✓ Wydruk na 1 dzień / cały tydzień + SMM",

        feat_4: "✓ Pełne TTK (woda, tłuszcze, przyprawy)", feat_5: "✓ Zero-Waste: zarządzanie stanem magazynu", feat_6: "✓ Organizator imprez i cateringu B2B",

        feat_7: "✓ Do 5 lokali w jednym panelu", feat_8: "✓ Zbiorcze zamówienia na magazyn centralny", feat_9: "✓ Eksport do iiko / Poster / R-Keeper",

        btn_try_free: "14 dni za darmo", btn_contact: "Zamów demo",

        lbl_yield: "Gramatura", lbl_unit_cost: "Koszt 1 porcji", lbl_batch_cost: "Partia dnia",

        th_ing: "Składnik", th_gross: "Brutto", th_waste: "Odpad", th_net: "Netto", th_cost: "Koszt", lbl_tech: "Technologia przygotowania",

        settings_title: "Ustawienia lokalu", lbl_venue_name: "Nazwa lokalu", lbl_currency: "Waluta", btn_save: "Zapisz",

        ai_loading: "AI-Chef odnawia bazę receptur i układa menu dopasowane do sezonu oraz magazynu...",

        cats: { meat: "Mięso i ryby", veg: "Warzywa i owoce", dairy: "Nabiał i jaja", dry: "Sypkie i kasze", liquids: "Płyny i tłuszcze", spices: "Przyprawy i zioła" }

      }

    };


    // Clone PL keys to other 9 languages with localized day names & currency defaults so all 10 languages work seamlessly

    const LANG_OVERRIDES = {

      ru: { countryBadge: "• RUB (₽)", currency: "₽", defaultFc: 260, defaultGuestPrice: 590, cuisinePill: "🇷🇺 Восточноевропейская и домашняя кухня", days: ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница"], tab_menu: "Меню (Пн–Пт)", tab_ttk: "Техкарты (ТТК)", tab_proc: "Закупки", tab_events: "Банкеты", tab_print: "Печать и SMM", tab_history: "История", tab_pricing: "Тарифы" },

      uk: { countryBadge: "• UAH (₴)", currency: "₴", defaultFc: 110, defaultGuestPrice: 240, cuisinePill: "🇺🇦 Українська та Європейська кухня", days: ["Понеділок", "Вівторок", "Середа", "Четвер", "П'ятниця"], tab_menu: "Меню (Пн–Пт)", tab_ttk: "Техкарти (ТТК)", tab_proc: "Закупівлі", tab_events: "Банкети", tab_print: "Друк та SMM", tab_history: "Історія", tab_pricing: "Тарифи" },

      it: { countryBadge: "• EUR (€)", currency: "€", defaultFc: 3.40, defaultGuestPrice: 13, cuisinePill: "🇮🇹 Cucina Italiana & Mediterranea", days: ["Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì"], tab_menu: "Menù (Lun–Ven)", tab_ttk: "Schede (TTK)", tab_proc: "Lista Spesa", tab_events: "Eventi & Banchetti", tab_print: "Stampa & SMM", tab_history: "Storico", tab_pricing: "Piani B2B" },

      es: { countryBadge: "• EUR (€)", currency: "€", defaultFc: 3.30, defaultGuestPrice: 12.50, cuisinePill: "🇪🇸 Cocina Española y Mediterránea", days: ["Lunes", "Martes", "Miércoles", "Jueves", "Viernes"], tab_menu: "Menú (Lun–Vie)", tab_ttk: "Escandallos", tab_proc: "Compras", tab_events: "Eventos & Catering", tab_print: "Imprimir & SMM", tab_history: "Historial", tab_pricing: "Planes B2B" },

      de: { countryBadge: "• EUR (€)", currency: "€", defaultFc: 3.60, defaultGuestPrice: 13.50, cuisinePill: "🇩🇪 Deutsche & Mitteleuropäische Küche", days: ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag"], tab_menu: "Menü (Mo–Fr)", tab_ttk: "Rezepturen", tab_proc: "Einkauf", tab_events: "Events & Catering", tab_print: "Druck & SMM", tab_history: "Verlauf", tab_pricing: "B2B Pläne" },

      fr: { countryBadge: "• EUR (€)", currency: "€", defaultFc: 3.70, defaultGuestPrice: 14, cuisinePill: "🇫🇷 Cuisine Française & Bistro", days: ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi"], tab_menu: "Menu (Lun–Ven)", tab_ttk: "Fiches (TTK)", tab_proc: "Achats", tab_events: "Événements", tab_print: "Impression & SMM", tab_history: "Historique", tab_pricing: "Plans B2B" },

      tr: { countryBadge: "• TRY (₺)", currency: "₺", defaultFc: 95, defaultGuestPrice: 280, cuisinePill: "🇹🇷 Türk & Akdeniz Mutfağı", days: ["Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma"], tab_menu: "Menü (Pzt–Cum)", tab_ttk: "Reçeteler", tab_proc: "Satın Alma", tab_events: "Etkinlik & Davet", tab_print: "Baskı & SMM", tab_history: "Geçmiş", tab_pricing: "Planlar" },

      he: { dir: "rtl", countryBadge: "• ILS (₪)", currency: "₪", defaultFc: 16.5, defaultGuestPrice: 58, cuisinePill: "🇮🇱 מטבח ישראלי וים-תיכוני", days: ["יום שני", "יום שלישי", "יום רביעי", "יום חמישי", "יום שישי"], tab_menu: "תפריט", tab_ttk: "מתכונים", tab_proc: "רכש", tab_events: "אירועים וקייטרינג", tab_print: "הדפסה", tab_history: "היסטוריה", tab_pricing: "תוכניות" },

      ar: { dir: "rtl", countryBadge: "• AED", currency: "AED", defaultFc: 16.0, defaultGuestPrice: 55, cuisinePill: "🇸🇦 المطبخ الشامي والعربي", days: ["الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة"], tab_menu: "القائمة", tab_ttk: "الوصفات", tab_proc: "المشتريات", tab_events: "الحفلات والولائم", tab_print: "الطباعة", tab_history: "السجل", tab_pricing: "الباقات" }

    };


    Object.keys(LANG_OVERRIDES).forEach(code => {

      I18N[code] = { ...I18N.pl, ...LANG_OVERRIDES[code] };

    });


    const ING_TRANS = {

      "Kiełbasa biała surowa": { ru: "Белая колбаса сырая", uk: "Біла ковбаса сира", it: "Salsiccia bianca", es: "Salchicha blanca", de: "Frische Bratwurst", fr: "Saucisse blanche", tr: "Taze sosis", he: "נקניקייה טרייה", ar: "سجق طازج" },

      "Boczek wędzony parzony": { ru: "Копченый бекон", uk: "Копчений бекон", it: "Pancetta affumicata", es: "Panceta ahumada", de: "Geräucherter Speck", fr: "Lard fumé", tr: "Füme et", he: "בשר מעושן", ar: "لحم مدخن" },

      "Dynia piżmowa świeża": { ru: "Тыква свежая", uk: "Гарбуз свіжий", it: "Zucca fresca", es: "Calabaza fresca", de: "Kürbis frisch", fr: "Potiron frais", tr: "Taze balkabağı", he: "דלעת טרייה", ar: "يقطين طازج" },

      "Polędwiczka wieprzowa": { ru: "Свиная вырезка", uk: "Свиняча вирізка", it: "Filetto di maiale", es: "Solomillo de cerdo", de: "Schweinefilet", fr: "Filet mignon de porc", tr: "Bonfile dilimleri", he: "פילה בקר / עגל", ar: "فيليه لحم طري" },

      "Ziemniaki jadalne": { ru: "Картофель свежий", uk: "Картопля свіжа", it: "Patate gialle", es: "Patatas frescas", de: "Speisekartoffeln", fr: "Pommes de terre", tr: "Taze patates", he: "תפוחי אדמה", ar: "بطاطس طازجة" },

      "Ćwiartka / udko z kurczaka": { ru: "Куриный окорочок", uk: "Куряче стегно", it: "Coscia di pollo", es: "Muslo de pollo", de: "Hähnchenkeule", fr: "Cuisse de poulet", tr: "Tavuk but", he: "כרע עוף", ar: "فخذ دجاج" },

      "Filet z piersi kurczaka": { ru: "Филе куриной грудки", uk: "Філе курячої грудки", it: "Petto di pollo", es: "Pechuga de pollo", de: "Hähnchenbrustfilet", fr: "Filet de poulet", tr: "Tavuk göğüs fileto", he: "חזה עוף פילה", ar: "فيليه صدر دجاج" },

      "Filet z dorsza atlantyckiego": { ru: "Филе трески", uk: "Філе тріски / хека", it: "Filetto di merluzzo", es: "Filete de merluza", de: "Kabeljaufilet", fr: "Dos de cabillaud", tr: "Mezgit fileto", he: "פילה דג בקלה", ar: "فيليه سمك القد" }

    };


    function trIng(name) {

      if (appState.lang === "pl") return name;

      return (ING_TRANS[name] && ING_TRANS[name][appState.lang]) ? ING_TRANS[name][appState.lang] : name;

    }


    // =========================================================================

    // 2. REGIONAL PROFILES & DYNAMIC SIDES

    // =========================================================================

    const REGIONAL_PROFILES = {

      pl: {

        venue: "Restauracja Studio Buffo",

        days: [

          {

            meatSoup: {

              title: "Żurek staropolski z białą kiełbasą, jajkiem i świeżym majerankiem",

              yield: "300 ml",

              seasons: ["autumn", "winter", "spring"],

              tech: "Wywar na wędzonce zaciągnąć zakwasem żytnim, doprawić roztartym w dłoniach majerankiem i czosnkiem. Podać z połówką jajka na twardo i plastrami białej kiełbasy.",

              ingredients: [

                { name: "Kiełbasa biała surowa", cat: "meat", gross: 65, waste: 5, net: 62, unit: "g", pricePerUnit: 0.028 },

                { name: "Boczek wędzony parzony", cat: "meat", gross: 25, waste: 8, net: 23, unit: "g", pricePerUnit: 0.032 },

                { name: "Zakwas żytni na żurek", cat: "liquids", gross: 80, waste: 0, net: 80, unit: "ml", pricePerUnit: 0.008 },

                { name: "Bulion mięsno-warzywny", cat: "liquids", gross: 220, waste: 0, net: 220, unit: "ml", pricePerUnit: 0.004 },

                { name: "Jajko kurze gotowane", cat: "dairy", gross: 30, waste: 12, net: 26, unit: "g", pricePerUnit: 0.018 },

                { name: "Śmietana 18%", cat: "dairy", gross: 20, waste: 0, net: 20, unit: "ml", pricePerUnit: 0.014 },

                { name: "Majeranek, czosnek, liść laurowy", cat: "spices", gross: 5, waste: 0, net: 5, unit: "g", pricePerUnit: 0.045 }

              ]

            },

            vegSoup: {

              title: "Krem z pieczonej dyni z mleczkiem kokosowym, oliwą imbirową i chipsem z jarmużu",

              yield: "300 ml",

              seasons: ["autumn", "winter"],

              tech: "Dynię upiec w piecu (190°C / 30 min) z oliwą i imbirem, zblendować z bulionem i mleczkiem kokosowym. Liście jarmużu podpiec (160°C / 7 min) na chrupiący chips do dekoracji kremu.",

              ingredients: [

                { name: "Dynia piżmowa świeża", cat: "veg", gross: 160, waste: 22, net: 125, unit: "g", pricePerUnit: 0.006 },

                { name: "Bulion warzywny", cat: "liquids", gross: 180, waste: 0, net: 180, unit: "ml", pricePerUnit: 0.003 },

                { name: "Mleczko kokosowe", cat: "liquids", gross: 40, waste: 0, net: 40, unit: "ml", pricePerUnit: 0.015 },

                { name: "Oliwa z oliwek", cat: "liquids", gross: 8, waste: 0, net: 8, unit: "ml", pricePerUnit: 0.038 },

                { name: "Pestki dyni łuskane", cat: "dry", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.034 },

                { name: "Imbir świeży, kurkuma, sól", cat: "spices", gross: 6, waste: 10, net: 5.4, unit: "g", pricePerUnit: 0.040 }

              ]

            },

            meatMain: {

              base: "Polędwiczka wieprzowa sous-vide w aksamitnym sosie kurkowym z zielonym pieprzem",

              side: "Ziemniaki gotowane z wody z koperkiem",

              salad: "Kapusta zasmażana z koperkiem (na ciepło)",

              yield: "430 g",

              seasons: ["autumn", "winter", "spring", "summer"],

              tech: "Sous-vide: 62°C / 90 min (polędwiczka zamarynowana z tymiankiem i czosnkiem). Obsmażyć na mocno rozgrzanym maśle (45 sek. z każdej strony) i podać z sosem śmietanowo-kurkowym.",

              ingredients: [

                { name: "Polędwiczka wieprzowa", cat: "meat", gross: 150, waste: 6, net: 141, unit: "g", pricePerUnit: 0.034 },

                { name: "Śmietanka 30% i kurki", cat: "dairy", gross: 65, waste: 5, net: 62, unit: "g", pricePerUnit: 0.026 },

                { name: "Olej rzepakowy i masło", cat: "liquids", gross: 18, waste: 0, net: 18, unit: "ml", pricePerUnit: 0.016 },

                { name: "Tymianek, zielony pieprz, czosnek", cat: "spices", gross: 7, waste: 5, net: 6.6, unit: "g", pricePerUnit: 0.042 },

                { name: "Ziemniaki jadalne", cat: "veg", gross: 210, waste: 22, net: 164, unit: "g", pricePerUnit: 0.003, _dynamicSide: true },

                { name: "Masło / oliwa i przyprawy do dodatku", cat: "spices", gross: 10, waste: 0, net: 10, unit: "g", pricePerUnit: 0.025, _dynamicSide: true },

                { name: "Warzywa / surówka dnia", cat: "veg", gross: 130, waste: 14, net: 112, unit: "g", pricePerUnit: 0.0055, _dynamicSide: true },

                { name: "Dressing, zioła i przyprawy korzenne", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.028, _dynamicSide: true }

              ]

            },

            vegMain: {

              base: "Placki ziemniaczane po zbójnicku z gulaszem z leśnych grzybów i śmietaną",

              side: "Śmietana 18% i szczypiorek",

              salad: "Surówka z białej kapusty z marchewką i jabłkiem",

              yield: "410 g",

              seasons: ["autumn", "winter"],

              tech: "Ziemniaki zetrzeć, odcisnąć i usmażyć na złociste, chrupiące placki. Wyłożyć na wierzch aromatyczny sos z pieczarek i grzybów leśnych z tymiankiem.",

              ingredients: [

                { name: "Ziemniaki na placki", cat: "veg", gross: 240, waste: 22, net: 187, unit: "g", pricePerUnit: 0.003 },

                { name: "Pieczarki i grzyby leśne", cat: "veg", gross: 85, waste: 10, net: 76, unit: "g", pricePerUnit: 0.019 },

                { name: "Śmietanka 30%", cat: "dairy", gross: 35, waste: 0, net: 35, unit: "ml", pricePerUnit: 0.018 },

                { name: "Olej do smażenia", cat: "liquids", gross: 25, waste: 0, net: 25, unit: "ml", pricePerUnit: 0.011 },

                { name: "Cebula, czosnek, pieprz, sól", cat: "spices", gross: 18, waste: 12, net: 16, unit: "g", pricePerUnit: 0.014 },

                { name: "Warzywa / surówka dnia", cat: "veg", gross: 130, waste: 14, net: 112, unit: "g", pricePerUnit: 0.0055, _dynamicSide: true },

                { name: "Dressing, zioła i przyprawy korzenne", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.028, _dynamicSide: true }

              ]

            }

          },

          {

            meatSoup: {

              title: "Barszcz ukraiński z czerwoną fasolą, kwaśną śmietaną i świeżym koperkiem",

              yield: "300 ml",

              seasons: ["autumn", "winter", "spring"],

              tech: "Esencjonalny wywar wołowy połączyć z pieczonymi burakami, czerwoną fasolą i warzywami korzeniowymi. Na wydawce wykończyć kleksem śmietany i posiekanym koperkiem.",

              ingredients: [

                { name: "Szponder / wołowina rosołowa", cat: "meat", gross: 70, waste: 22, net: 55, unit: "g", pricePerUnit: 0.028 },

                { name: "Buraki czerwone", cat: "veg", gross: 95, waste: 18, net: 78, unit: "g", pricePerUnit: 0.004 },

                { name: "Fasola czerwona i warzywa", cat: "dry", gross: 65, waste: 10, net: 59, unit: "g", pricePerUnit: 0.009 },

                { name: "Woda / wywar", cat: "liquids", gross: 230, waste: 0, net: 230, unit: "ml", pricePerUnit: 0.001 },

                { name: "Śmietana 18%", cat: "dairy", gross: 20, waste: 0, net: 20, unit: "ml", pricePerUnit: 0.014 },

                { name: "Liść laurowy, ziele ang., ocet", cat: "spices", gross: 6, waste: 0, net: 6, unit: "g", pricePerUnit: 0.032 }

              ]

            },

            vegSoup: {

              title: "Zupa grzybowa z podgrzybków z łazankami i świeżą natką pietruszki",

              yield: "300 ml",

              seasons: ["autumn", "winter"],

              tech: "Grzyby leśne przesmażyć z tymiankiem i cebulką, gotować na wywarze warzywnym, zabielić śmietaną i podać z domowymi łazankami oraz natką pietruszki.",

              ingredients: [

                { name: "Grzyby leśne / podgrzybki", cat: "veg", gross: 70, waste: 12, net: 62, unit: "g", pricePerUnit: 0.032 },

                { name: "Łazanki makaronowe", cat: "dry", gross: 35, waste: 0, net: 35, unit: "g", pricePerUnit: 0.010 },

                { name: "Bulion warzywny", cat: "liquids", gross: 220, waste: 0, net: 220, unit: "ml", pricePerUnit: 0.003 },

                { name: "Śmietana 18%", cat: "dairy", gross: 20, waste: 0, net: 20, unit: "ml", pricePerUnit: 0.014 },

                { name: "Tymianek, liść laurowy, pieprz", cat: "spices", gross: 5, waste: 0, net: 5, unit: "g", pricePerUnit: 0.038 }

              ]

            },

            meatMain: {

              base: "Soczyste udko z kurczaka pieczone w ziołach i wędzonej papryce z sosem pieczeniowym",

              side: "Kasza gryczana prażona na sypko",

              salad: "Buraczki zasmażane na ciepło",

              yield: "450 g",

              seasons: ["autumn", "winter", "spring", "summer"],

              tech: "Pieczenie: 185°C / 38 min (pierwsze 25 min pod przykryciem, ostatnie 13 min z funkcją termoobiegu dla chrupiącej skórki). Zredukować sos z pieczeni z masłem.",

              ingredients: [

                { name: "Ćwiartka / udko z kurczaka", cat: "meat", gross: 220, waste: 26, net: 163, unit: "g", pricePerUnit: 0.016 },

                { name: "Olej rzepakowy i masło", cat: "liquids", gross: 16, waste: 0, net: 16, unit: "ml", pricePerUnit: 0.018 },

                { name: "Papryka słodka, czosnek, majeranek", cat: "spices", gross: 7, waste: 0, net: 7, unit: "g", pricePerUnit: 0.038 },

                { name: "Kasza gryczana", cat: "dry", gross: 85, waste: 0, net: 85, unit: "g", pricePerUnit: 0.009, _dynamicSide: true },

                { name: "Masło / oliwa i przyprawy do dodatku", cat: "spices", gross: 10, waste: 0, net: 10, unit: "g", pricePerUnit: 0.025, _dynamicSide: true },

                { name: "Warzywa / surówka dnia", cat: "veg", gross: 130, waste: 14, net: 112, unit: "g", pricePerUnit: 0.0055, _dynamicSide: true },

                { name: "Dressing, zioła i przyprawy korzenne", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.028, _dynamicSide: true }

              ]

            },

            vegMain: {

              base: "Kotlety z kaszy jaglanej i pieczarek w aksamitnym sosie koperkowym",

              side: "Ziemniaki puree z masłem",

              salad: "Mizeria ze świeżych ogórków ze śmietaną",

              yield: "420 g",

              seasons: ["spring", "summer", "autumn", "winter"],

              tech: "Kaszę jaglaną połączyć z podsmażoną cebulą i pieczarkami, uformować kotleciki, opiec na złoto i polać śmietanowym sosem koperkowym.",

              ingredients: [

                { name: "Kasza jaglana sucha", cat: "dry", gross: 75, waste: 0, net: 75, unit: "g", pricePerUnit: 0.010 },

                { name: "Pieczarki świeże", cat: "veg", gross: 90, waste: 10, net: 81, unit: "g", pricePerUnit: 0.014 },

                { name: "Śmietana 18%", cat: "dairy", gross: 35, waste: 0, net: 35, unit: "ml", pricePerUnit: 0.014 },

                { name: "Olej, koperek, sól, pieprz", cat: "spices", gross: 16, waste: 5, net: 15, unit: "g", pricePerUnit: 0.024 },

                { name: "Ziemniaki jadalne", cat: "veg", gross: 210, waste: 22, net: 164, unit: "g", pricePerUnit: 0.003, _dynamicSide: true },

                { name: "Masło / oliwa i przyprawy do dodatku", cat: "spices", gross: 10, waste: 0, net: 10, unit: "g", pricePerUnit: 0.025, _dynamicSide: true },

                { name: "Warzywa / surówka dnia", cat: "veg", gross: 130, waste: 14, net: 112, unit: "g", pricePerUnit: 0.0055, _dynamicSide: true },

                { name: "Dressing, zioła i przyprawy korzenne", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.028, _dynamicSide: true }

              ]

            }

          },

          {

            meatSoup: {

              title: "Krupnik domowy z pęczakiem, warzywami i świeżym lubczykiem",

              yield: "300 ml",

              seasons: ["autumn", "winter", "spring"],

              tech: "Wywar z udka kurczaka gotować 60 min na wolnym ogniu z włoszczyzną, ziemniakami i kaszą pęczak, na koniec doprawić świeżym lubczykiem i natką pietruszki.",

              ingredients: [

                { name: "Ćwiartka / udko z kurczaka", cat: "meat", gross: 75, waste: 26, net: 56, unit: "g", pricePerUnit: 0.016 },

                { name: "Kasza jęczmienna pęczak", cat: "dry", gross: 35, waste: 0, net: 35, unit: "g", pricePerUnit: 0.007 },

                { name: "Włoszczyzna i ziemniaki", cat: "veg", gross: 95, waste: 18, net: 78, unit: "g", pricePerUnit: 0.005 },

                { name: "Woda / wywar", cat: "liquids", gross: 240, waste: 0, net: 240, unit: "ml", pricePerUnit: 0.001 },

                { name: "Natka pietruszki, ziele ang., sól", cat: "spices", gross: 6, waste: 10, net: 5.4, unit: "g", pricePerUnit: 0.035 }

              ]

            },

            vegSoup: {

              title: "Krem z pomidorów pelati z pieczonym czosnkiem, oliwą bazyliową i chipsem z parmezanu",

              yield: "300 ml",

              seasons: ["spring", "summer", "autumn", "winter"],

              tech: "Czosnek upiec w główce (180°C / 25 min), zblendować z pomidorami pelati i bulionem. Tartą mozzarellę/parmezan zapiec na pergaminie (190°C / 6 min) na chrupiący chips do kremu.",

              ingredients: [

                { name: "Pomidory pelati w puszce", cat: "veg", gross: 185, waste: 0, net: 185, unit: "g", pricePerUnit: 0.009 },

                { name: "Bulion warzywny", cat: "liquids", gross: 120, waste: 0, net: 120, unit: "ml", pricePerUnit: 0.003 },

                { name: "Mozzarella mini", cat: "dairy", gross: 25, waste: 0, net: 25, unit: "g", pricePerUnit: 0.034 },

                { name: "Oliwa extra virgin", cat: "liquids", gross: 10, waste: 0, net: 10, unit: "ml", pricePerUnit: 0.038 },

                { name: "Bazylia świeża, oregano, czosnek", cat: "spices", gross: 7, waste: 10, net: 6.3, unit: "g", pricePerUnit: 0.045 }

              ]

            },

            meatMain: {

              base: "Gulasz z łopatki wieprzowej duszony z wędzoną papryką, kminkiem i czosnkiem",

              side: "Kasza pęczak z ziołami",

              salad: "Marchewka z groszkiem na ciepło",

              yield: "440 g",

              seasons: ["autumn", "winter"],

              tech: "Mięso obsmażyć na złoto, dusić powoli z cebulą, wędzoną papryką, czosnkiem i kminkiem do pełnej miękkości.",

              ingredients: [

                { name: "Łopatka wieprzowa b/k", cat: "meat", gross: 165, waste: 8, net: 152, unit: "g", pricePerUnit: 0.023 },

                { name: "Olej i woda / wywar", cat: "liquids", gross: 85, waste: 0, net: 85, unit: "ml", pricePerUnit: 0.004 },

                { name: "Papryka wędzona, kminek, czosnek", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.042 },

                { name: "Kasza pęczak", cat: "dry", gross: 85, waste: 0, net: 85, unit: "g", pricePerUnit: 0.007, _dynamicSide: true },

                { name: "Masło / oliwa i przyprawy do dodatku", cat: "spices", gross: 10, waste: 0, net: 10, unit: "g", pricePerUnit: 0.025, _dynamicSide: true },

                { name: "Warzywa / surówka dnia", cat: "veg", gross: 130, waste: 14, net: 112, unit: "g", pricePerUnit: 0.0055, _dynamicSide: true },

                { name: "Dressing, zioła i przyprawy korzenne", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.028, _dynamicSide: true }

              ]

            },

            vegMain: {

              base: "Makaron penne w kremowym sosie ze szpinakiem, suszonymi pomidorami i parmezanem",

              side: "Świeżo tarty parmezan, prażony słonecznik i oliwa bazyliowa",

              salad: "Lekka sałatka z pomidorków cherry, rukoli i prażonych pestek w sosie winegret",

              yield: "410 g",

              seasons: ["spring", "summer", "autumn", "winter"],

              tech: "Makaron al dente połączyć ze świeżym szpinakiem, suszonymi pomidorami, czosnkiem, śmietanką i świeżo tartą gałką muszkatołową.",

              ingredients: [

                { name: "Makaron penne semolina", cat: "dry", gross: 105, waste: 0, net: 105, unit: "g", pricePerUnit: 0.009 },

                { name: "Szpinak świeży baby", cat: "veg", gross: 80, waste: 5, net: 76, unit: "g", pricePerUnit: 0.024 },

                { name: "Śmietanka 30% i ser tarty", cat: "dairy", gross: 65, waste: 0, net: 65, unit: "g", pricePerUnit: 0.024 },

                { name: "Oliwa, czosnek, gałka muszkatołowa", cat: "spices", gross: 13, waste: 0, net: 13, unit: "g", pricePerUnit: 0.035 },

                { name: "Warzywa / surówka dnia", cat: "veg", gross: 130, waste: 14, net: 112, unit: "g", pricePerUnit: 0.0055, _dynamicSide: true },

                { name: "Dressing, zioła i przyprawy korzenne", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.028, _dynamicSide: true }

              ]

            }

          },

          {

            meatSoup: {

              title: "Rosół królewski z paloną cebulą, lubczykiem i domowym makaronem",

              yield: "300 ml",

              seasons: ["autumn", "winter", "spring", "summer"],

              tech: "Klarowny bulion gotować 4 godziny na wolnym ogniu z opalaną cebulą, włoszczyzną, lubczykiem i zielem angielskim.",

              ingredients: [

                { name: "Mięso rosołowe drobiowo-wołowe", cat: "meat", gross: 85, waste: 28, net: 61, unit: "g", pricePerUnit: 0.021 },

                { name: "Włoszczyzna świeża", cat: "veg", gross: 65, waste: 20, net: 52, unit: "g", pricePerUnit: 0.005 },

                { name: "Makaron nitki jajeczny", cat: "dry", gross: 30, waste: 0, net: 30, unit: "g", pricePerUnit: 0.013 },

                { name: "Woda źródlana", cat: "liquids", gross: 260, waste: 0, net: 260, unit: "ml", pricePerUnit: 0.001 },

                { name: "Lubczyk, liść laurowy, pieprz ziarn.", cat: "spices", gross: 5, waste: 0, net: 5, unit: "g", pricePerUnit: 0.040 }

              ]

            },

            vegSoup: {

              title: "Krem z zielonego groszku ze świeżą miętą, oliwą ziołową i chipsem z selera",

              yield: "300 ml",

              seasons: ["spring", "summer"],

              tech: "Groszek ugotować krótko na bulionie warzywnym ze świeżą miętą, zblendować na gładki krem. Cienkie plastry selera upiec (175°C / 14 min) na chrupiący chips do kremu.",

              ingredients: [

                { name: "Groszek zielony mrożony", cat: "veg", gross: 135, waste: 0, net: 135, unit: "g", pricePerUnit: 0.011 },

                { name: "Bulion warzywny", cat: "liquids", gross: 200, waste: 0, net: 200, unit: "ml", pricePerUnit: 0.003 },

                { name: "Masło klarowane / oliwa", cat: "liquids", gross: 10, waste: 0, net: 10, unit: "ml", pricePerUnit: 0.032 },

                { name: "Włoszczyzna świeża", cat: "veg", gross: 30, waste: 15, net: 25, unit: "g", pricePerUnit: 0.005 },

                { name: "Mięta świeża, sól, pieprz biały", cat: "spices", gross: 5, waste: 10, net: 4.5, unit: "g", pricePerUnit: 0.045 }

              ]

            },

            meatMain: {

              base: "Pierś z kurczaka sous-vide z masłem ziołowym, suszonymi pomidorami i bazylią",

              side: "Ryż basmati na parze",

              salad: "Surówka z białej kapusty z marchewką i jabłkiem",

              yield: "430 g",

              seasons: ["spring", "summer", "autumn", "winter"],

              tech: "Sous-vide: 63°C / 75 min (pierś z kurczaka zamknięta próżniowo z oliwą, czosnkiem i rozmarynem). Szybko zrumienić na patelni i podać z emulsją z suszonych pomidorów.",

              ingredients: [

                { name: "Filet z piersi kurczaka", cat: "meat", gross: 165, waste: 5, net: 157, unit: "g", pricePerUnit: 0.025 },

                { name: "Śmietanka 30% i suszone pomidory", cat: "dairy", gross: 55, waste: 0, net: 55, unit: "g", pricePerUnit: 0.024 },

                { name: "Olej, bazylia, czosnek, rozmaryn", cat: "spices", gross: 15, waste: 0, net: 15, unit: "g", pricePerUnit: 0.032 },

                { name: "Ryż basmati", cat: "dry", gross: 80, waste: 0, net: 80, unit: "g", pricePerUnit: 0.011, _dynamicSide: true },

                { name: "Masło / oliwa i przyprawy do dodatku", cat: "spices", gross: 10, waste: 0, net: 10, unit: "g", pricePerUnit: 0.025, _dynamicSide: true },

                { name: "Warzywa / surówka dnia", cat: "veg", gross: 130, waste: 14, net: 112, unit: "g", pricePerUnit: 0.0055, _dynamicSide: true },

                { name: "Dressing, zioła i przyprawy korzenne", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.028, _dynamicSide: true }

              ]

            },

            vegMain: {

              base: "Pierogi ruskie z twarogiem i pieczonym ziemniakiem okraszone karmelizowaną cebulką",

              side: "Karmelizowana cebulka z tymiankiem i świeżo mielonym pieprzem",

              salad: "Lekka sałatka z pomidorków cherry, rukoli i prażonych pestek w sosie winegret",

              yield: "420 g",

              seasons: ["autumn", "winter", "spring"],

              tech: "Pierogi ugotować al dente, podsmażyć lekko na maśle i podać ze słodką, wolno zeszkloną cebulką ze świeżo mielonym pieprzem.",

              ingredients: [

                { name: "Pierogi ruskie ręcznie lepione", cat: "dry", gross: 285, waste: 0, net: 285, unit: "g", pricePerUnit: 0.018 },

                { name: "Cebula na okrasę", cat: "veg", gross: 50, waste: 12, net: 44, unit: "g", pricePerUnit: 0.004 },

                { name: "Olej rzepakowy", cat: "liquids", gross: 16, waste: 0, net: 16, unit: "ml", pricePerUnit: 0.011 },

                { name: "Pieprz czarny świeżo mielony, sól", cat: "spices", gross: 4, waste: 0, net: 4, unit: "g", pricePerUnit: 0.032 },

                { name: "Warzywa / surówka dnia", cat: "veg", gross: 130, waste: 14, net: 112, unit: "g", pricePerUnit: 0.0055, _dynamicSide: true },

                { name: "Dressing, zioła i przyprawy korzenne", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.028, _dynamicSide: true }

              ]

            }

          },

          {

            meatSoup: {

              title: "Zupa gulaszowa po węgiersku z papryką i kminkiem",

              yield: "300 ml",

              seasons: ["autumn", "winter"],

              tech: "Wołowinę obsmażyć na złoto, dusić ze słodką i wędzoną papryką, czosnkiem oraz kminkiem w esencjonalnym wywarze wołowym.",

              ingredients: [

                { name: "Wołowina gulaszowa", cat: "meat", gross: 75, waste: 8, net: 69, unit: "g", pricePerUnit: 0.036 },

                { name: "Papryka czerwona i ziemniaki", cat: "veg", gross: 95, waste: 18, net: 78, unit: "g", pricePerUnit: 0.008 },

                { name: "Wywar wołowy", cat: "liquids", gross: 210, waste: 0, net: 210, unit: "ml", pricePerUnit: 0.004 },

                { name: "Olej rzepakowy", cat: "liquids", gross: 8, waste: 0, net: 8, unit: "ml", pricePerUnit: 0.011 },

                { name: "Papryka słodka, ostra, kminek", cat: "spices", gross: 7, waste: 0, net: 7, unit: "g", pricePerUnit: 0.042 }

              ]

            },

            vegSoup: {

              title: "Zupa z czerwonej soczewicy i pieczonych pomidorów z kminem rzymskim i kolendrą",

              yield: "300 ml",

              seasons: ["autumn", "winter", "spring"],

              tech: "Czerwoną soczewicę ugotować na bulionie warzywnym z pomidorami pelati, prażonym kminem rzymskim i czosnkiem. Podać ze świeżą kolendrą i kroplą oliwy.",

              ingredients: [

                { name: "Soczewica czerwona", cat: "dry", gross: 55, waste: 0, net: 55, unit: "g", pricePerUnit: 0.013 },

                { name: "Pomidory krojone pelati", cat: "veg", gross: 85, waste: 0, net: 85, unit: "g", pricePerUnit: 0.009 },

                { name: "Bulion warzywny", cat: "liquids", gross: 210, waste: 0, net: 210, unit: "ml", pricePerUnit: 0.003 },

                { name: "Oliwa z oliwek", cat: "liquids", gross: 8, waste: 0, net: 8, unit: "ml", pricePerUnit: 0.038 },

                { name: "Kmin rzymski, kolendra, czosnek", cat: "spices", gross: 6, waste: 0, net: 6, unit: "g", pricePerUnit: 0.045 }

              ]

            },

            meatMain: {

              base: "Filet z dorsza z pieca w ziołowo-cytrynowej kruszonce z sosem maślano-koperkowym",

              side: "Ziemniaki gotowane z wody z koperkiem",

              salad: "Mizeria w stylu skandynawskim z koperkiem, olejem rzepakowym tłoczonym na zimno i czerwonym pieprzem",

              yield: "440 g",

              seasons: ["spring", "summer", "autumn", "winter"],

              tech: "Pieczenie: 190°C / 14 min (ryba skropiona cytryną pod ziołową kruszonką z masłem). Podawać z ciepłym sosem maślano-koperkowym.",

              ingredients: [

                { name: "Filet z dorsza atlantyckiego", cat: "meat", gross: 155, waste: 5, net: 147, unit: "g", pricePerUnit: 0.038 },

                { name: "Olej do smażenia i masło", cat: "liquids", gross: 22, waste: 0, net: 22, unit: "ml", pricePerUnit: 0.015 },

                { name: "Cytryna, koperek, pieprz cytrynowy", cat: "spices", gross: 13, waste: 15, net: 11, unit: "g", pricePerUnit: 0.026 },

                { name: "Ziemniaki jadalne", cat: "veg", gross: 210, waste: 22, net: 164, unit: "g", pricePerUnit: 0.003, _dynamicSide: true },

                { name: "Masło / oliwa i przyprawy do dodatku", cat: "spices", gross: 10, waste: 0, net: 10, unit: "g", pricePerUnit: 0.025, _dynamicSide: true },

                { name: "Warzywa / surówka dnia", cat: "veg", gross: 130, waste: 14, net: 112, unit: "g", pricePerUnit: 0.0055, _dynamicSide: true },

                { name: "Dressing, zioła i przyprawy korzenne", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.028, _dynamicSide: true }

              ]

            },

            vegMain: {

              base: "Naleśniki zapiekane z waniliowym twarogiem w ciepłym sosie śmietankowo-cynamonowym",

              side: "Prażone płatki migdałów, natka pietruszki i oliwa czosnkowa",

              salad: "Sałatka owocowa z jabłkiem, pomarańczą i listkiem świeżej mięty",

              yield: "400 g",

              seasons: ["spring", "summer", "autumn", "winter"],

              tech: "Zapiekane w piecu: 180°C / 12 min pod kołderką ze śmietanki waniliowej i masła. Podawać z ciepłym musem jabłkowo-cynamonowym.",

              ingredients: [

                { name: "Twaróg półtłusty mielony", cat: "dairy", gross: 135, waste: 0, net: 135, unit: "g", pricePerUnit: 0.018 },

                { name: "Mąka pszenna, mleko i jajko", cat: "dry", gross: 145, waste: 2, net: 142, unit: "g", pricePerUnit: 0.008 },

                { name: "Masło klarowane", cat: "liquids", gross: 12, waste: 0, net: 12, unit: "ml", pricePerUnit: 0.034 },

                { name: "Cukier waniliowy, cynamon, mięta", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.030 },

                { name: "Warzywa / surówka dnia", cat: "veg", gross: 130, waste: 14, net: 112, unit: "g", pricePerUnit: 0.0055, _dynamicSide: true },

                { name: "Dressing, zioła i przyprawy korzenne", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.028, _dynamicSide: true }

              ]

            }

          }

        ]

      }

    };


    const COUNTRY_META = {

      ru: { venue: "Ресторан «Центральный»", scale: 19.5 },

      uk: { venue: "Ресторан «Смачна Кухня»", scale: 8.2 },

      it: { venue: "Trattoria Bella Tavola", scale: 0.25 },

      es: { venue: "Bistró El Mercado", scale: 0.24 },

      de: { venue: "Gasthaus Stadtküche", scale: 0.27 },

      fr: { venue: "Le Petit Bistrot", scale: 0.28 },

      tr: { venue: "Lokanta Anadolu", scale: 7.1 },

      he: { venue: "ביסטרו השוק", scale: 1.22 },

      ar: { venue: "مطعم الواحة", scale: 1.18 }

    };


    function buildLocalizedProfile(langCode) {

      const base = JSON.parse(JSON.stringify(REGIONAL_PROFILES.pl));

      if (!langCode || langCode === "pl") return base;

      const meta = COUNTRY_META[langCode] || { venue: "Lunch Pilot Bistro", scale: 0.25 };

      base.venue = meta.venue;

      base.days.forEach(day => {

        ["meatSoup", "vegSoup", "meatMain", "vegMain"].forEach(slot => {

          day[slot].ingredients.forEach(ing => {

            ing.pricePerUnit = +(ing.pricePerUnit * meta.scale).toFixed(4);

          });

        });

      });

      return base;

    }


    const SALAD_SIDE_POOL = {

      pl: [

        "Pieczone buraki glazurowane w miodzie, tymianku i occie balsamicznym z prażonym słonecznikiem",

        "Młoda kapusta duszona z koperkiem, wędzoną papryką i odrobiną masła palonego",

        "Marchewka karmelizowana z kminem rzymskim, imbirem, pomarańczą i kolendrą",

        "Surówka z czerwonej kapusty z jabłkiem, żurawiną i dressingiem musztardowo-miodowym",

        "Smażona cukinia i fasolka szparagowa z czosnkiem, skórką cytrynową i płatkami chili",

        "Mizeria w stylu skandynawskim z koperkiem, olejem rzepakowym tłoczonym na zimno i czerwonym pieprzem",

        "Surówka z białej kapusty z marchwią, porem i prażonym sezamem w sosie winegret",

        "Seler i jabłko julienne z orzechami włoskimi, natką pietruszki i jogurtem greckim"

      ]

    };


    const STARCH_SIDE_POOL = {

      pl: [

        { name: "Puree ziemniaczane z palonym masłem, chrzanem i szczypiorkiem", ingName: "Ziemniaki jadalne", gross: 210, waste: 22, unit: "g", price: 0.003, spice: "Chrzan tarty, masło, gałka muszkatołowa", spicePrice: 0.025 },

        { name: "Kasza gryczana prażona na bulionie grzybowym z tymiankiem", ingName: "Kasza gryczana", gross: 85, waste: 0, unit: "g", price: 0.009, spice: "Tymianek, czosnek pieczony, pieprz", spicePrice: 0.032 },

        { name: "Ziemniaki opiekane w rozmarynie, czosnku i wędzonej papryce", ingName: "Ziemniaki jadalne", gross: 220, waste: 20, unit: "g", price: 0.003, spice: "Rozmaryn, papryka wędzona, czosnek", spicePrice: 0.035 },

        { name: "Ryż jaśminowy z kurkumą, prażonym kminem i skórką limonki", ingName: "Ryż basmati", gross: 80, waste: 0, unit: "g", price: 0.011, spice: "Kurkuma, kmin rzymski, limonka", spicePrice: 0.038 },

        { name: "Kasza pęczakotto z białym winem, natką pietruszki i parmezanem", ingName: "Kasza pęczak", gross: 85, waste: 0, unit: "g", price: 0.007, spice: "Natka pietruszki, tymianek, pieprz biały", spicePrice: 0.030 },

        { name: "Kasza bulgur z suszonymi pomidorami, oregano i oliwą cytrynową", ingName: "Kasza jaglana sucha", gross: 80, waste: 0, unit: "g", price: 0.010, spice: "Oregano, suszone pomidory, skórka cytryny", spicePrice: 0.036 },

        { name: "Kopytka ziemniaczane podsmażane na złoto z szałwią i czosnkiem", ingName: "Ziemniaki na placki", gross: 220, waste: 20, unit: "g", price: 0.003, spice: "Szałwia świeża, gałka muszkatołowa, sól", spicePrice: 0.034 },

        { name: "Polenta kremowa z ziołami prowansalskimi i prażonym słonecznikiem", ingName: "Kasza jaglana sucha", gross: 80, waste: 0, unit: "g", price: 0.008, spice: "Zioła prowansalskie, rozmaryn, pieprz", spicePrice: 0.030 }

      ]

    };


    const GARNISH_ONLY_TOPPINGS = {

      pl: [

        { name: "Świeżo tarty parmezan, prażony słonecznik i oliwa bazyliowa", isGarnishOnly: true },

        { name: "Karmelizowana cebulka z tymiankiem i świeżo mielonym pieprzem", isGarnishOnly: true },

        { name: "Kwaśna śmietana 18% ze szczypiorkiem i prażoną pestką dyni", isGarnishOnly: true },

        { name: "Prażone płatki migdałów, natka pietruszki i oliwa czosnkowa", isGarnishOnly: true }

      ]

    };


    const LIGHT_SALAD_ONLY = {

      pl: [

        "Lekka sałatka z pomidorków cherry, rukoli i prażonych pestek w sosie winegret",

        "Świeża sałatka z ogórka, rzodkiewki i koperku z oliwą cytrynową",

        "Chrupiąca sałata rzymska z pomidorem, czerwoną cebulką i dressingiem ziołowym",

        "Sałatka owocowa z jabłkiem, pomarańczą i listkiem świeżej mięty"

      ]

    };


    // =========================================================================

    // 3. CULINARY PAIRING & AUTOMATIC RECIPE RECALCULATION ENGINE

    // =========================================================================

    const SELF_CONTAINED_KEYWORDS = ["makaron", "penne", "lasagne", "pierogi", "kopytka", "placki", "naleśnik", "risotto", "rösti"];

    const FISH_KEYWORDS = ["dorsz", "łosoś", "sandacz", "pstrąg", "ryb"];

    const CURRY_ASIAN_KEYWORDS = ["curry", "kokos", "imbir", "kurkum", "masala", "tikka"];

    const SWEET_DISH_KEYWORDS = ["naleśnik", "twarog", "wanili", "mus owoc"];


    function detectDishCategory(dishBaseText) {

      const low = (dishBaseText || "").toLowerCase();

      if (SWEET_DISH_KEYWORDS.some(k => low.includes(k))) return "sweet_flour";

      if (SELF_CONTAINED_KEYWORDS.some(k => low.includes(k))) return "self_contained_starch";

      if (FISH_KEYWORDS.some(k => low.includes(k))) return "fish";

      if (CURRY_ASIAN_KEYWORDS.some(k => low.includes(k))) return "curry_asian";

      return "classic_savory";

    }


    function inferStarchIngredientFromText(sideText) {

      const low = (sideText || "").toLowerCase();

      const pool = STARCH_SIDE_POOL.pl;

      const exact = pool.find(s => s.name.toLowerCase() === low);

      if (exact) return exact;


      if (low.includes("parmezan") || low.includes("cebul") || low.includes("śmietan") || low.includes("migdał") || low.includes("oliwa")) {

        return { name: sideText, isGarnishOnly: true };

      }

      if (low.includes("gryczan")) {

        return { name: sideText, ingName: "Kasza gryczana", gross: 85, waste: 0, unit: "g", price: 0.009, spice: "Tymianek, czosnek, pieprz", spicePrice: 0.032 };

      }

      if (low.includes("pęczak")) {

        return { name: sideText, ingName: "Kasza pęczak", gross: 85, waste: 0, unit: "g", price: 0.007, spice: "Natka pietruszki, tymianek", spicePrice: 0.030 };

      }

      if (low.includes("ryż") || low.includes("basmati") || low.includes("jaśmin")) {

        return { name: sideText, ingName: "Ryż basmati", gross: 80, waste: 0, unit: "g", price: 0.011, spice: "Kurkuma, kmin rzymski", spicePrice: 0.038 };

      }

      if (low.includes("bulgur") || low.includes("jaglan") || low.includes("kuskus")) {

        return { name: sideText, ingName: "Kasza jaglana sucha", gross: 80, waste: 0, unit: "g", price: 0.010, spice: "Oregano, oliwa, zioła", spicePrice: 0.035 };

      }

      if (low.includes("kopytk") || low.includes("klusk") || low.includes("gnocchi")) {

        return { name: sideText, ingName: "Ziemniaki na placki", gross: 220, waste: 20, unit: "g", price: 0.003, spice: "Szałwia, masło, gałka muszkatołowa", spicePrice: 0.034 };

      }

      return { name: sideText, ingName: "Ziemniaki jadalne", gross: 210, waste: 22, unit: "g", price: 0.003, spice: "Masło, koperek, przyprawy", spicePrice: 0.025 };

    }


    function syncMainSideIngredients(dishObj, starchItem, saladText) {

      const metaScale = (appState.lang === "pl") ? 1 : ((COUNTRY_META[appState.lang]?.scale) || 0.25);

      const coreIngredients = dishObj.ingredients.filter(ing => !ing._dynamicSide);

      const dynamicRows = [];


      if (starchItem && !starchItem.isGarnishOnly) {

        const netStarch = Math.round(starchItem.gross * (1 - (starchItem.waste || 0) / 100));

        dynamicRows.push({

          name: starchItem.ingName,

          cat: starchItem.ingName.includes("Ziemniaki") ? "veg" : "dry",

          gross: starchItem.gross,

          waste: starchItem.waste || 0,

          net: netStarch,

          unit: starchItem.unit || "g",

          pricePerUnit: +((starchItem.price || 0.005) * metaScale).toFixed(4),

          _dynamicSide: true

        });

        dynamicRows.push({

          name: "Masło / oliwa i przyprawy do dodatku",

          cat: "spices",

          gross: 10,

          waste: 0,

          net: 10,

          unit: "g",

          pricePerUnit: +((starchItem.spicePrice || 0.025) * metaScale).toFixed(4),

          _dynamicSide: true

        });

      } else if (starchItem && starchItem.isGarnishOnly) {

        dynamicRows.push({

          name: "Dressing, zioła i przyprawy korzenne",

          cat: "spices",

          gross: 12,

          waste: 0,

          net: 12,

          unit: "g",

          pricePerUnit: +(0.032 * metaScale).toFixed(4),

          _dynamicSide: true

        });

      }


      if (saladText && saladText.trim().length > 2) {

        dynamicRows.push({

          name: "Warzywa / surówka dnia",

          cat: "veg",

          gross: 130,

          waste: 14,

          net: 112,

          unit: "g",

          pricePerUnit: +(0.0055 * metaScale).toFixed(4),

          _dynamicSide: true

        });

        dynamicRows.push({

          name: "Dressing, zioła i przyprawy korzenne",

          cat: "spices",

          gross: 8,

          waste: 0,

          net: 8,

          unit: "g",

          pricePerUnit: +(0.028 * metaScale).toFixed(4),

          _dynamicSide: true

        });

      }


      dishObj.ingredients = [...coreIngredients, ...dynamicRows];

    }


    function getCompatibleSidesForDish(dishBaseText) {

      const cat = detectDishCategory(dishBaseText);

      const allStarch = STARCH_SIDE_POOL.pl;

      const allSalads = SALAD_SIDE_POOL.pl;

      const garnishes = GARNISH_ONLY_TOPPINGS.pl;

      const lightSalads = LIGHT_SALAD_ONLY.pl;


      if (cat === "sweet_flour") {

        return { cat, starches: [garnishes[3] || garnishes[0]], salads: [lightSalads[3] || lightSalads[0]] };

      }

      if (cat === "self_contained_starch") {

        return { cat, starches: garnishes, salads: lightSalads };

      }

      if (cat === "fish") {

        const fishStarches = allStarch.filter(s => !s.name.toLowerCase().includes("gryczan") && !s.name.toLowerCase().includes("polenta"));

        const fishSalads = allSalads.filter(s => !s.toLowerCase().includes("burak"));

        return { cat, starches: fishStarches.length ? fishStarches : allStarch, salads: fishSalads.length ? fishSalads : allSalads };

      }

      if (cat === "curry_asian") {

        const asianStarches = allStarch.filter(s => s.name.toLowerCase().includes("ryż") || s.name.toLowerCase().includes("bulgur"));

        const asianSalads = allSalads.filter(s => !s.toLowerCase().includes("kapusta zasmażana") && !s.toLowerCase().includes("buraczki zasmażane"));

        return { cat, starches: asianStarches.length ? asianStarches : allStarch, salads: asianSalads.length ? asianSalads : allSalads };

      }

      return { cat, starches: allStarch, salads: allSalads };

    }


    function verifyAndHarmonizeDish(dishObj, forceRefreshSides = false) {

      const compat = getCompatibleSidesForDish(dishObj.base);

      let changed = false;


      const validStarchNames = new Set(compat.starches.map(s => s.name));

      const validSaladNames = new Set(compat.salads);


      let chosenStarch = compat.starches.find(s => s.name === dishObj.side);

      if (!chosenStarch || !validStarchNames.has(dishObj.side) || forceRefreshSides) {

        chosenStarch = compat.starches[Math.floor(Math.random() * compat.starches.length)];

        dishObj.side = chosenStarch.name;

        changed = true;

      }


      let chosenSalad = validSaladNames.has(dishObj.salad) ? dishObj.salad : null;

      if (!chosenSalad || forceRefreshSides) {

        chosenSalad = compat.salads[Math.floor(Math.random() * compat.salads.length)];

        dishObj.salad = chosenSalad;

        changed = true;

      }


      syncMainSideIngredients(dishObj, chosenStarch, chosenSalad);

      return changed;

    }


    function runFullMenuAudit(showUserToast = false) {

      let fixedCount = 0;

      appState.profile.days.forEach(day => {

        if (verifyAndHarmonizeDish(day.meatMain, false)) fixedCount++;

        if (verifyAndHarmonizeDish(day.vegMain, false)) fixedCount++;

      });


      const auditTextEl = document.getElementById("culinary-audit-text");

      if (auditTextEl) {

        auditTextEl.textContent = fixedCount > 0

          ? t("audit_fixed_tpl").replace("{n}", fixedCount)

          : t("audit_ok_tpl");

      }


      if (fixedCount > 0) recalculateAll();

      if (showUserToast) {

        showToast(fixedCount > 0 ? "✅ " + t("audit_fixed_tpl").replace("{n}", fixedCount) : "✅ " + t("audit_ok_tpl"));

      }

      return fixedCount;

    }


    // =========================================================================

    // 4. APPLICATION STATE & PERSISTENCE

    // =========================================================================

    function escapeHtml(str) {

      return String(str ?? "")

        .replace(/&/g, "&amp;")

        .replace(/</g, "&lt;")

        .replace(/>/g, "&gt;")

        .replace(/"/g, "&quot;")

        .replace(/'/g, "&#39;");

    }


    function addDaysToIso(isoDate, daysToAdd) {

      try {

        const parts = (isoDate || "").split("-").map(Number);

        const dt = parts.length === 3 ? new Date(parts[0], parts[1] - 1, parts[2]) : new Date();

        dt.setDate(dt.getDate() + daysToAdd);

        const y = dt.getFullYear();

        const m = String(dt.getMonth() + 1).padStart(2, "0");

        const d = String(dt.getDate()).padStart(2, "0");

        return `${y}-${m}-${d}`;

      } catch (e) {

        return isoDate || "2026-11-16";

      }

    }


    function getNextOrCurrentMonday() {

      const now = new Date();

      const day = now.getDay();

      const diff = day === 0 ? 1 : (day === 1 ? 0 : 8 - day);

      now.setDate(now.getDate() + diff);

      return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;

    }


    function getElVal(id, fallback = "") {

      const el = document.getElementById(id);

      return el ? el.value : fallback;

    }


    function setElVal(id, val) {

      const el = document.getElementById(id);

      if (el && val !== undefined && val !== null) el.value = val;

    }


    function t(key) {

      const dict = I18N[appState.lang] || I18N.pl;

      return dict[key] !== undefined ? dict[key] : (I18N.pl[key] || key);

    }


    function formatMoney(val) {

      const num = Number(val) || 0;

      const cur = appState.currency || "zł";

      if (cur === "zł" || cur === "₽" || cur === "₴" || cur === "₺") {

        return `${num.toFixed(2)} ${cur}`;

      }

      return `${cur} ${num.toFixed(2)}`;

    }


    function showToast(msg) {

      const el = document.getElementById("toast-notification");

      if (!el) return;

      el.textContent = msg;

      el.classList.remove("hidden");

      clearTimeout(window._toastTimer);

      window._toastTimer = setTimeout(() => el.classList.add("hidden"), 3400);

    }


    const appState = {

      lang: "pl",

      currency: "zł",

      venueName: "Restauracja Studio Buffo",

      weekStartDate: getNextOrCurrentMonday(),

      activeTab: "generator",

      ttkMode: "single",

      ttkSelectedDay: 0,

      procurementSplit: "all",

      printScope: "week",

      printDayIndex: 0,

      profile: buildLocalizedProfile("pl"),

      historyLog: [],

      aiCustomPool: { meatSoup: [], vegSoup: [], meatMain: [], vegMain: [] },

      sessionSeenDishes: new Set(),

      eventCourses: [

        { type: "Przystawka", title: "Tatar z pieczonego buraka z kozim serem, orzechem włoskim i oliwą truflową", costPerGuest: 7.40, allergens: "Laktoza, Orzechy" },

        { type: "Zupa", title: "Krem z pieczonych białych warzyw i gruszki z oliwą rozmarynową", costPerGuest: 5.20, allergens: "Seler, Laktoza" },

        { type: "Danie główne", title: "Polędwiczka wieprzowa sous-vide w sosie kurkowym z puree chrzanowym i glazurowaną marchewką", costPerGuest: 16.80, allergens: "Laktoza" },

        { type: "Bufet zimny", title: "Półmisek mięs pieczonych (karkówka z czosnkiem confit, indyk w ziołach) + sałatki sezonowe", costPerGuest: 12.50, allergens: "Gorczyca" },

        { type: "Deser", title: "Sernik baskijski z musem z czarnej porzeczki i prażonymi migdałami", costPerGuest: 6.10, allergens: "Gluten, Jaja, Laktoza, Orzechy" }

      ]

    };


    // =========================================================================

    // 5. PROCEDURAL & AI-CHEF DISH DATABASE (UNLIMITED VARIETY)

    // =========================================================================

    const PROCEDURAL_DISHES = {

      meatSoup: [

        {

          title: "Kwaśnica góralska na wędzonce z ziemniakami i prażonym kminkiem",

          yield: "300 ml", seasons: ["autumn", "winter"],

          tech: "Kapustę kiszoną gotować na esencjonalnym wywarze wędzonym z kminkiem, czosnkiem i zielem angielskim. Podać z kostką ziemniaków.",

          ingredients: [

            { name: "Golonka / boczek wędzony", cat: "meat", gross: 70, waste: 15, net: 60, unit: "g", pricePerUnit: 0.029 },

            { name: "Kapusta kiszona", cat: "veg", gross: 95, waste: 0, net: 95, unit: "g", pricePerUnit: 0.007 },

            { name: "Ziemniaki jadalne", cat: "veg", gross: 65, waste: 20, net: 52, unit: "g", pricePerUnit: 0.003 },

            { name: "Woda / wywar", cat: "liquids", gross: 230, waste: 0, net: 230, unit: "ml", pricePerUnit: 0.001 },

            { name: "Kminek, czosnek, liść laurowy", cat: "spices", gross: 5, waste: 0, net: 5, unit: "g", pricePerUnit: 0.036 }

          ]

        },

        {

          title: "Zupa ogórkowa z tartymi ogórkami kiszonymi, koperkiem i pulpecikami drobiowymi",

          yield: "300 ml", seasons: ["autumn", "winter", "spring"],

          tech: "Bulion warzywny połączyć z podsmażonymi na maśle ogórkami kiszonymi, ziemniakami i mini pulpecikami z indyka. Zabielić śmietaną.",

          ingredients: [

            { name: "Mięso mielone z indyka", cat: "meat", gross: 60, waste: 0, net: 60, unit: "g", pricePerUnit: 0.024 },

            { name: "Ogórki kiszone tarte", cat: "veg", gross: 85, waste: 5, net: 81, unit: "g", pricePerUnit: 0.009 },

            { name: "Ziemniaki i włoszczyzna", cat: "veg", gross: 70, waste: 20, net: 56, unit: "g", pricePerUnit: 0.004 },

            { name: "Śmietana 18%", cat: "dairy", gross: 20, waste: 0, net: 20, unit: "ml", pricePerUnit: 0.014 },

            { name: "Koperek świeży, ziele ang., pieprz", cat: "spices", gross: 5, waste: 5, net: 4.7, unit: "g", pricePerUnit: 0.035 }

          ]

        },

        {

          title: "Zupa serowa z porem, chrupiącym boczkiem i grzankami ziołowymi",

          yield: "300 ml", seasons: ["autumn", "winter"],

          tech: "Białą część pora zeszklić na maśle, zalać bulionem, rozpuścić ser topiony i cheddar. Podać z wysmażonym boczkiem.",

          ingredients: [

            { name: "Boczek wędzony parzony", cat: "meat", gross: 45, waste: 8, net: 41, unit: "g", pricePerUnit: 0.032 },

            { name: "Ser cheddar i topiony", cat: "dairy", gross: 55, waste: 0, net: 55, unit: "g", pricePerUnit: 0.034 },

            { name: "Por świeży i ziemniaki", cat: "veg", gross: 90, waste: 18, net: 74, unit: "g", pricePerUnit: 0.006 },

            { name: "Bulion warzywny", cat: "liquids", gross: 210, waste: 0, net: 210, unit: "ml", pricePerUnit: 0.003 },

            { name: "Gałka muszkatołowa, tymianek, pieprz", cat: "spices", gross: 4, waste: 0, net: 4, unit: "g", pricePerUnit: 0.042 }

          ]

        },

        {

          title: "Solanka domowa z szynką, kiełbasą, oliwkami, kaparami i cytryną",

          yield: "300 ml", seasons: ["autumn", "winter", "spring"],

          tech: "Wędliny przesmażyć z koncentratem pomidorowym i ogórkiem kiszonym, dusić w bulionie z oliwkami i kaparami.",

          ingredients: [

            { name: "Szynka wędzona i kiełbasa", cat: "meat", gross: 75, waste: 4, net: 72, unit: "g", pricePerUnit: 0.029 },

            { name: "Ogórki kiszone i oliwki", cat: "veg", gross: 65, waste: 5, net: 62, unit: "g", pricePerUnit: 0.014 },

            { name: "Koncentrat pomidorowy", cat: "veg", gross: 25, waste: 0, net: 25, unit: "g", pricePerUnit: 0.012 },

            { name: "Bulion mięsno-warzywny", cat: "liquids", gross: 220, waste: 0, net: 220, unit: "ml", pricePerUnit: 0.004 },

            { name: "Cytryna, kapary, natka pietruszki", cat: "spices", gross: 10, waste: 15, net: 8.5, unit: "g", pricePerUnit: 0.032 }

          ]

        },

        {

          title: "Grochówka wojskowa z wędzonym boczkiem, kiełbasą i majerankiem",

          yield: "300 ml", seasons: ["autumn", "winter"],

          tech: "Namoczony groch łuskany ugotować do miękkości z wędzonym boczkiem, ziemniakami i przesmażoną cebulką z majerankiem.",

          ingredients: [

            { name: "Boczek wędzony i kiełbasa", cat: "meat", gross: 70, waste: 6, net: 66, unit: "g", pricePerUnit: 0.030 },

            { name: "Groch łuskany żółty", cat: "dry", gross: 55, waste: 0, net: 55, unit: "g", pricePerUnit: 0.008 },

            { name: "Ziemniaki i marchew", cat: "veg", gross: 75, waste: 20, net: 60, unit: "g", pricePerUnit: 0.004 },

            { name: "Woda / wywar", cat: "liquids", gross: 220, waste: 0, net: 220, unit: "ml", pricePerUnit: 0.001 },

            { name: "Majeranek, czosnek, pieprz czarny", cat: "spices", gross: 6, waste: 0, net: 6, unit: "g", pricePerUnit: 0.040 }

          ]

        },

        {

          title: "Zupa harira marokańska z kurczakiem, ciecierzycą, pomidorami i kolendrą",

          yield: "300 ml", seasons: ["autumn", "winter", "spring"],

          tech: "Kurczaka obsmażyć z cynamonem, kminem i imbirem, dusić w pomidorach z ciecierzycą, soczewicą i świeżą kolendrą.",

          ingredients: [

            { name: "Filet z udka kurczaka", cat: "meat", gross: 65, waste: 5, net: 62, unit: "g", pricePerUnit: 0.023 },

            { name: "Pomidory pelati i ciecierzyca", cat: "veg", gross: 110, waste: 0, net: 110, unit: "g", pricePerUnit: 0.011 },

            { name: "Bulion drobiowy", cat: "liquids", gross: 200, waste: 0, net: 200, unit: "ml", pricePerUnit: 0.004 },

            { name: "Oliwa z oliwek", cat: "liquids", gross: 8, waste: 0, net: 8, unit: "ml", pricePerUnit: 0.038 },

            { name: "Kmin rzymski, cynamon, kolendra", cat: "spices", gross: 6, waste: 0, net: 6, unit: "g", pricePerUnit: 0.045 }

          ]

        },

        {

          title: "Zupa rybna śródziemnomorska z łososiem, dorszem, koprem włoskim i szafranem",

          yield: "300 ml", seasons: ["spring", "summer", "autumn", "winter"],

          tech: "Wywar warzywno-pomidorowy z fenkułem zagotować, włożyć kostki łososia i dorsza na 5 minut przed wydaniem.",

          ingredients: [

            { name: "Dorsz i łosoś (kawałki)", cat: "meat", gross: 75, waste: 5, net: 71, unit: "g", pricePerUnit: 0.042 },

            { name: "Pomidory pelati i fenkuł", cat: "veg", gross: 95, waste: 12, net: 84, unit: "g", pricePerUnit: 0.012 },

            { name: "Bulion rybno-warzywny", cat: "liquids", gross: 210, waste: 0, net: 210, unit: "ml", pricePerUnit: 0.004 },

            { name: "Oliwa extra virgin", cat: "liquids", gross: 8, waste: 0, net: 8, unit: "ml", pricePerUnit: 0.038 },

            { name: "Czosnek, natka, tymianek", cat: "spices", gross: 5, waste: 0, net: 5, unit: "g", pricePerUnit: 0.038 }

          ]

        }

      ],

      vegSoup: [

        {

          title: "Krem z pieczonego kalafiora i czosnku z oliwą truflową i chipsem z pasternaku",

          yield: "300 ml", seasons: ["autumn", "winter", "spring"],

          tech: "Różyczki kalafiora upiec (195°C / 25 min) z czosnkiem, zblendować z bulionem i śmietanką. Pasternak pokroić w cienkie plastry i upiec (175°C / 12 min) na chips do kremu.",

          ingredients: [

            { name: "Kalafior świeży", cat: "veg", gross: 170, waste: 28, net: 122, unit: "g", pricePerUnit: 0.008 },

            { name: "Bulion warzywny", cat: "liquids", gross: 190, waste: 0, net: 190, unit: "ml", pricePerUnit: 0.003 },

            { name: "Śmietanka 30%", cat: "dairy", gross: 25, waste: 0, net: 25, unit: "ml", pricePerUnit: 0.018 },

            { name: "Oliwa smakowa i pasternak", cat: "veg", gross: 25, waste: 15, net: 21, unit: "g", pricePerUnit: 0.018 },

            { name: "Gałka muszkatołowa, biały pieprz", cat: "spices", gross: 4, waste: 0, net: 4, unit: "g", pricePerUnit: 0.042 }

          ]

        },

        {

          title: "Krem z pieczonych batatów i marchewki z mleczkiem kokosowym i chipsem z jarmużu",

          yield: "300 ml", seasons: ["autumn", "winter"],

          tech: "Bataty i marchew upiec (190°C / 28 min) z imbirem i pomarańczą, zblendować na gładki krem. Podać z chipsem z jarmużu (160°C / 7 min).",

          ingredients: [

            { name: "Bataty i marchew świeża", cat: "veg", gross: 165, waste: 18, net: 135, unit: "g", pricePerUnit: 0.008 },

            { name: "Mleczko kokosowe", cat: "liquids", gross: 45, waste: 0, net: 45, unit: "ml", pricePerUnit: 0.015 },

            { name: "Bulion warzywny", cat: "liquids", gross: 180, waste: 0, net: 180, unit: "ml", pricePerUnit: 0.003 },

            { name: "Jarmuż świeży i olej", cat: "veg", gross: 20, waste: 15, net: 17, unit: "g", pricePerUnit: 0.016 },

            { name: "Imbir, curry łagodne, sok z pomarańczy", cat: "spices", gross: 8, waste: 5, net: 7.6, unit: "g", pricePerUnit: 0.038 }

          ]

        },

        {

          title: "Włoska zupa minestrone z młodą cukinią, białą fasolą i bazyliowym pesto",

          yield: "300 ml", seasons: ["spring", "summer", "autumn"],

          tech: "Drobno pokrojone warzywa sezonowe gotować krótko w bulionie pomidorowym z fasolą cannellini i drobnym makaronem. Wykończyć łyżeczką pesto.",

          ingredients: [

            { name: "Cukinia, marchew, seler naciowy", cat: "veg", gross: 120, waste: 14, net: 103, unit: "g", pricePerUnit: 0.007 },

            { name: "Fasola biała i pomidory pelati", cat: "dry", gross: 85, waste: 0, net: 85, unit: "g", pricePerUnit: 0.010 },

            { name: "Bulion warzywny", cat: "liquids", gross: 200, waste: 0, net: 200, unit: "ml", pricePerUnit: 0.003 },

            { name: "Pesto bazyliowe", cat: "spices", gross: 12, waste: 0, net: 12, unit: "g", pricePerUnit: 0.038 },

            { name: "Oregano, bazylia, pieprz czarny", cat: "spices", gross: 4, waste: 0, net: 4, unit: "g", pricePerUnit: 0.040 }

          ]

        },

        {

          title: "Flaczki z boczniaków królewskich z majerankiem, imbirem i wędzoną papryką",

          yield: "300 ml", seasons: ["autumn", "winter"],

          tech: "Paski boczniaków przesmażyć na maśle klarowanym z włoszczyzną, dusić w esencjonalnym bulionie z majerankiem, gałką i imbirem.",

          ingredients: [

            { name: "Boczniaki świeże", cat: "veg", gross: 115, waste: 8, net: 106, unit: "g", pricePerUnit: 0.018 },

            { name: "Włoszczyzna świeża", cat: "veg", gross: 65, waste: 18, net: 53, unit: "g", pricePerUnit: 0.005 },

            { name: "Bulion warzywny", cat: "liquids", gross: 220, waste: 0, net: 220, unit: "ml", pricePerUnit: 0.003 },

            { name: "Masło klarowane", cat: "liquids", gross: 10, waste: 0, net: 10, unit: "ml", pricePerUnit: 0.034 },

            { name: "Majeranek, imbir, gałka, papryka wędzona", cat: "spices", gross: 7, waste: 0, net: 7, unit: "g", pricePerUnit: 0.042 }

          ]

        },

        {

          title: "Chłodnik litewski na kefirze z młodą botwinką, ogórkiem, rzodkiewką i jajkiem",

          yield: "300 ml", seasons: ["spring", "summer"],

          tech: "Ugotowaną i wystudzoną botwinkę połączyć z gęstym kefirem, śmietaną, świeżym ogórkiem, rzodkiewką i dużą ilością koperku.",

          ingredients: [

            { name: "Botwinka świeża", cat: "veg", gross: 95, waste: 15, net: 81, unit: "g", pricePerUnit: 0.009 },

            { name: "Kefir naturalny i śmietana", cat: "dairy", gross: 185, waste: 0, net: 185, unit: "ml", pricePerUnit: 0.009 },

            { name: "Ogórek zielony i rzodkiewka", cat: "veg", gross: 65, waste: 10, net: 58, unit: "g", pricePerUnit: 0.008 },

            { name: "Jajko kurze gotowane", cat: "dairy", gross: 30, waste: 12, net: 26, unit: "g", pricePerUnit: 0.018 },

            { name: "Koperek, szczypiorek, sok z cytryny", cat: "spices", gross: 8, waste: 5, net: 7.6, unit: "g", pricePerUnit: 0.032 }

          ]

        },

        {

          title: "Krem z białych szparagów i młodego ziemniaka z oliwą koperkową i chipsem z parmezanu",

          yield: "300 ml", seasons: ["spring", "summer"],

          tech: "Szparagi ugotować na bulionie warzywnym z młodym ziemniakiem, zblendować na aksamitny krem i podać z chipsem z parmezanu (190°C / 6 min).",

          ingredients: [

            { name: "Szparagi białe lub zielone", cat: "veg", gross: 135, waste: 22, net: 105, unit: "g", pricePerUnit: 0.022 },

            { name: "Ziemniaki młode", cat: "veg", gross: 60, waste: 12, net: 53, unit: "g", pricePerUnit: 0.004 },

            { name: "Bulion warzywny", cat: "liquids", gross: 190, waste: 0, net: 190, unit: "ml", pricePerUnit: 0.003 },

            { name: "Parmezan tarty i śmietanka", cat: "dairy", gross: 25, waste: 0, net: 25, unit: "g", pricePerUnit: 0.032 },

            { name: "Gałka muszkatołowa, koperek, biały pieprz", cat: "spices", gross: 5, waste: 0, net: 5, unit: "g", pricePerUnit: 0.042 }

          ]

        }

      ],

      meatMain: [

        {

          base: "Karkówka wieprzowa sous-vide z czosnkiem confit w ciemnym sosie pieczeniowo-majerankowym",

          side: "Puree ziemniaczane z palonym masłem, chrzanem i szczypiorkiem",

          salad: "Pieczone buraki glazurowane w miodzie, tymianku i occie balsamicznym z prażonym słonecznikiem",

          yield: "440 g", seasons: ["autumn", "winter", "spring"],

          tech: "Sous-vide: 68°C / 12 godz. (karkówka natarta majerankiem, czosnkiem i gorczycą). Obsmażyć przed wydaniem i polać sosem pieczeniowym.",

          ingredients: [

            { name: "Karkówka wieprzowa b/k", cat: "meat", gross: 170, waste: 6, net: 160, unit: "g", pricePerUnit: 0.025 },

            { name: "Sos pieczeniowy demi-glace", cat: "liquids", gross: 60, waste: 0, net: 60, unit: "ml", pricePerUnit: 0.012 },

            { name: "Majeranek, czosnek, musztarda", cat: "spices", gross: 10, waste: 0, net: 10, unit: "g", pricePerUnit: 0.035 }

          ]

        },

        {

          base: "Gołąbki domowe w młodej kapuście z mięsem i ryżem w sosie z pieczonych pomidorów z lubczykiem",

          side: "Ziemniaki gotowane z wody z koperkiem",

          salad: "Marchewka karmelizowana z kminem rzymskim, imbirem, pomarańczą i kolendrą",

          yield: "460 g", seasons: ["spring", "summer", "autumn", "winter"],

          tech: "Pieczenie w sosie: 175°C / 45 min pod przykryciem w esencjonalnym sosie pomidorowo-ziołowym z masłem.",

          ingredients: [

            { name: "Łopatka wieprzowa mielona", cat: "meat", gross: 135, waste: 0, net: 135, unit: "g", pricePerUnit: 0.023 },

            { name: "Kapusta biała / młoda", cat: "veg", gross: 130, waste: 18, net: 107, unit: "g", pricePerUnit: 0.005 },

            { name: "Pomidory pelati i ryż", cat: "veg", gross: 110, waste: 0, net: 110, unit: "g", pricePerUnit: 0.009 },

            { name: "Lubczyk, majeranek, pieprz, masło", cat: "spices", gross: 12, waste: 0, net: 12, unit: "g", pricePerUnit: 0.032 }

          ]

        },

        {

          base: "Kotlet schabowy z kostką smażony na maśle klarowanym z cytryną i świeżym koperkiem",

          side: "Ziemniaki opiekane w rozmarynie, czosnku i wędzonej papryce",

          salad: "Młoda kapusta duszona z koperkiem, wędzoną papryką i odrobiną masła palonego",

          yield: "450 g", seasons: ["autumn", "winter", "spring", "summer"],

          tech: "Schab rozbić, panierować w jajku i bułce panko, smażyć na maśle klarowanym na złoty kolor (po 3 min z każdej strony).",

          ingredients: [

            { name: "Schab wieprzowy", cat: "meat", gross: 165, waste: 8, net: 152, unit: "g", pricePerUnit: 0.026 },

            { name: "Jajko i panierka panko", cat: "dry", gross: 45, waste: 0, net: 45, unit: "g", pricePerUnit: 0.014 },

            { name: "Masło klarowane / olej", cat: "liquids", gross: 25, waste: 0, net: 25, unit: "ml", pricePerUnit: 0.024 },

            { name: "Cytryna, pieprz czarny, sól", cat: "spices", gross: 8, waste: 10, net: 7.2, unit: "g", pricePerUnit: 0.025 }

          ]

        },

        {

          base: "Pulpety z indyka w aksamitnym sosie koperkowo-cytrynowym z zielonym groszkiem",

          side: "Kasza pęczakotto z białym winem, natką pietruszki i parmezanem",

          salad: "Surówka z czerwonej kapusty z jabłkiem, żurawiną i dressingiem musztardowo-miodowym",

          yield: "430 g", seasons: ["spring", "summer", "autumn", "winter"],

          tech: "Delikatne pulpety z indyka dusić w bulionie drobiowym, wykończyć śmietanką, świeżym koperkiem i sokiem z cytryny.",

          ingredients: [

            { name: "Mięso mielone z piersi indyka", cat: "meat", gross: 155, waste: 0, net: 155, unit: "g", pricePerUnit: 0.025 },

            { name: "Śmietanka 30% i bulion", cat: "dairy", gross: 75, waste: 0, net: 75, unit: "ml", pricePerUnit: 0.015 },

            { name: "Koperek świeży, cytryna, gałka", cat: "spices", gross: 10, waste: 5, net: 9.5, unit: "g", pricePerUnit: 0.036 }

          ]

        },

        {

          base: "Curry z kurczakiem w mleczku kokosowym z trawą cytrynową, imbirem i limonką",

          side: "Ryż jaśminowy z kurkumą, prażonym kminem i skórką limonki",

          salad: "Mizeria w stylu skandynawskim z koperkiem, olejem rzepakowym tłoczonym na zimno i czerwonym pieprzem",

          yield: "435 g", seasons: ["spring", "summer", "autumn", "winter"],

          tech: "Filet z kurczaka obsmażyć z pastą curry, imbirem i czosnkiem, dusić w mleczku kokosowym z liśćmi limonki i kolendrą.",

          ingredients: [

            { name: "Filet z piersi kurczaka", cat: "meat", gross: 160, waste: 4, net: 154, unit: "g", pricePerUnit: 0.025 },

            { name: "Mleczko kokosowe 18%", cat: "liquids", gross: 85, waste: 0, net: 85, unit: "ml", pricePerUnit: 0.015 },

            { name: "Pasta curry, imbir, kolendra, limonka", cat: "spices", gross: 15, waste: 10, net: 13.5, unit: "g", pricePerUnit: 0.042 }

          ]

        },

        {

          base: "Bitki wołowe duszone w sosie własnym z suszonymi grzybami i tymiankiem",

          side: "Kasza gryczana prażona na bulionie grzybowym z tymiankiem",

          salad: "Pieczone buraki glazurowane w miodzie, tymianku i occie balsamicznym z prażonym słonecznikiem",

          yield: "440 g", seasons: ["autumn", "winter"],

          tech: "Plastry wołowiny obsmażyć na mocno rozgrzanym tłuszczu, dusić 90 minut z grzybami leśnymi, cebulką i tymiankiem.",

          ingredients: [

            { name: "Wołowina zrazowa / ligawa", cat: "meat", gross: 165, waste: 8, net: 152, unit: "g", pricePerUnit: 0.039 },

            { name: "Grzyby suszone i cebula", cat: "veg", gross: 45, waste: 10, net: 40, unit: "g", pricePerUnit: 0.025 },

            { name: "Wywar wołowy i olej", cat: "liquids", gross: 85, waste: 0, net: 85, unit: "ml", pricePerUnit: 0.006 },

            { name: "Tymianek, liść laurowy, pieprz", cat: "spices", gross: 6, waste: 0, net: 6, unit: "g", pricePerUnit: 0.040 }

          ]

        }

      ],

      vegMain: [

        {

          base: "Stek z pieczonego kalafiora w przyprawach masala na kremie z białej fasoli z oliwą ziołową",

          side: "Kasza bulgur z suszonymi pomidorami, oregano i oliwą cytrynową",

          salad: "Marchewka karmelizowana z kminem rzymskim, imbirem, pomarańczą i kolendrą",

          yield: "420 g", seasons: ["autumn", "winter", "spring", "summer"],

          tech: "Pieczenie: 200°C / 24 min (plastry kalafiora natarte oliwą, kminem, kurkumą i wędzoną papryką). Podać na ciepłym kremie z fasoli.",

          ingredients: [

            { name: "Kalafior świeży (steki)", cat: "veg", gross: 210, waste: 24, net: 160, unit: "g", pricePerUnit: 0.008 },

            { name: "Fasola biała gotowana", cat: "dry", gross: 95, waste: 0, net: 95, unit: "g", pricePerUnit: 0.011 },

            { name: "Oliwa extra virgin", cat: "liquids", gross: 16, waste: 0, net: 16, unit: "ml", pricePerUnit: 0.038 },

            { name: "Kmin rzymski, kurkuma, papryka wędzona", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.045 }

          ]

        },

        {

          base: "Kopytka ziemniaczane zapiekane w sosie z leśnych grzybów, szpinaku i parmezanu",

          side: "Świeżo tarty parmezan, prażony słonecznik i oliwa bazyliowa",

          salad: "Lekka sałatka z pomidorków cherry, rukoli i prażonych pestek w sosie winegret",

          yield: "415 g", seasons: ["autumn", "winter", "spring"],

          tech: "Zapiekane w piecu: 190°C / 14 min pod kremowym sosem grzybowo-szpinakowym ze świeżo tartym parmezanem.",

          ingredients: [

            { name: "Kopytka ziemniaczane", cat: "dry", gross: 230, waste: 0, net: 230, unit: "g", pricePerUnit: 0.011 },

            { name: "Pieczarki brunatne i szpinak", cat: "veg", gross: 95, waste: 8, net: 87, unit: "g", pricePerUnit: 0.018 },

            { name: "Śmietanka 30% i parmezan", cat: "dairy", gross: 55, waste: 0, net: 55, unit: "g", pricePerUnit: 0.026 },

            { name: "Tymianek, czosnek, gałka muszkatołowa", cat: "spices", gross: 7, waste: 0, net: 7, unit: "g", pricePerUnit: 0.038 }

          ]

        },

        {

          base: "Curry z ciecierzycy, pieczonej dyni i szpinaku w mleczku kokosowym z kolendrą",

          side: "Ryż jaśminowy z kurkumą, prażonym kminem i skórką limonki",

          salad: "Świeża sałatka z ogórka, rzodkiewki i koperku z oliwą cytrynową",

          yield: "425 g", seasons: ["autumn", "winter", "spring", "summer"],

          tech: "Dynię podpiec (190°C / 20 min), dusić w mleczku kokosowym z ciecierzycą, pomidorami, imbirem, kurkumą i świeżym szpinakiem.",

          ingredients: [

            { name: "Ciecierzyca gotowana i dynia", cat: "veg", gross: 185, waste: 12, net: 163, unit: "g", pricePerUnit: 0.010 },

            { name: "Mleczko kokosowe", cat: "liquids", gross: 85, waste: 0, net: 85, unit: "ml", pricePerUnit: 0.015 },

            { name: "Szpinak świeży baby", cat: "veg", gross: 40, waste: 5, net: 38, unit: "g", pricePerUnit: 0.024 },

            { name: "Curry, imbir, kmin rzymski, kolendra", cat: "spices", gross: 10, waste: 0, net: 10, unit: "g", pricePerUnit: 0.044 }

          ]

        },

        {

          base: "Gołąbki wegetariańskie z kaszą gryczaną, borowikami i suszonymi pomidorami w sosie grzybowym",

          side: "Puree ziemniaczane z palonym masłem, chrzanem i szczypiorkiem",

          salad: "Pieczone buraki glazurowane w miodzie, tymianku i occie balsamicznym z prażonym słonecznikiem",

          yield: "445 g", seasons: ["autumn", "winter", "spring"],

          tech: "Pieczenie: 180°C / 40 min pod przykryciem w aromatycznym sosie grzybowym z tymiankiem i śmietanką.",

          ingredients: [

            { name: "Kapusta biała / włoska", cat: "veg", gross: 135, waste: 18, net: 111, unit: "g", pricePerUnit: 0.005 },

            { name: "Kasza gryczana i grzyby leśne", cat: "dry", gross: 110, waste: 5, net: 105, unit: "g", pricePerUnit: 0.017 },

            { name: "Śmietanka 30% i bulion warzywny", cat: "dairy", gross: 75, waste: 0, net: 75, unit: "ml", pricePerUnit: 0.014 },

            { name: "Tymianek, czosnek, majeranek, pieprz", cat: "spices", gross: 8, waste: 0, net: 8, unit: "g", pricePerUnit: 0.038 }

          ]

        },

        {

          base: "Lasagne warzywna z pieczoną cukinią, bakłażanem, ricottą i sosem pomidorowo-bazyliowym",

          side: "Świeżo tarty parmezan, prażony słonecznik i oliwa bazyliowa",

          salad: "Chrupiąca sałata rzymska z pomidorem, czerwoną cebulką i dressingiem ziołowym",

          yield: "420 g", seasons: ["spring", "summer", "autumn", "winter"],

          tech: "Pieczenie: 185°C / 32 min (warstwy makaronu lasagne przekładane grillowanymi warzywami, sosem pelati, ricottą i mozzarellą).",

          ingredients: [

            { name: "Płaty lasagne semolina", cat: "dry", gross: 95, waste: 0, net: 95, unit: "g", pricePerUnit: 0.012 },

            { name: "Cukinia, bakłażan i pomidory pelati", cat: "veg", gross: 175, waste: 12, net: 154, unit: "g", pricePerUnit: 0.009 },

            { name: "Ricotta i mozzarella", cat: "dairy", gross: 75, waste: 0, net: 75, unit: "g", pricePerUnit: 0.026 },

            { name: "Bazylia, oregano, czosnek, oliwa", cat: "spices", gross: 12, waste: 0, net: 12, unit: "g", pricePerUnit: 0.038 }

          ]

        },

        {

          base: "Risotto z pęczaku z pieczoną dynią, szałwią, kozim serem i prażonymi pestkami dyni",

          side: "Prażone płatki migdałów, natka pietruszki i oliwa czosnkowa",

          salad: "Lekka sałatka z pomidorków cherry, rukoli i prażonych pestek w sosie winegret",

          yield: "410 g", seasons: ["autumn", "winter"],

          tech: "Pęczak gotować metodą risotto na gorącym bulionie warzywnym z pieczoną dynią, masłem, świeżą szałwią i kozim serem.",

          ingredients: [

            { name: "Kasza jęczmienna pęczak", cat: "dry", gross: 95, waste: 0, net: 95, unit: "g", pricePerUnit: 0.007 },

            { name: "Dynia piżmowa pieczona", cat: "veg", gross: 120, waste: 20, net: 96, unit: "g", pricePerUnit: 0.006 },

            { name: "Ser kozi rolada i masło", cat: "dairy", gross: 45, waste: 0, net: 45, unit: "g", pricePerUnit: 0.036 },

            { name: "Szałwia świeża, tymianek, pestki dyni", cat: "spices", gross: 12, waste: 0, net: 12, unit: "g", pricePerUnit: 0.038 }

          ]

        }

      ]

    };


    function buildSlotPool(slotKey) {

      const metaScale = (appState.lang === "pl") ? 1 : ((COUNTRY_META[appState.lang]?.scale) || 0.25);

      const fromDays = appState.profile.days.map(d => d[slotKey]).filter(Boolean);

      const fromExtra = (PROCEDURAL_DISHES[slotKey] || []).map(item => {

        const clone = JSON.parse(JSON.stringify(item));

        clone.ingredients.forEach(ing => {

          ing.pricePerUnit = +(ing.pricePerUnit * metaScale).toFixed(4);

        });

        return clone;

      });

      const fromAi = (appState.aiCustomPool[slotKey] || []).map(item => JSON.parse(JSON.stringify(item)));


      const mapById = new Map();

      [...fromDays, ...fromExtra, ...fromAi].forEach(d => {

        const id = getDishIdentifier(d);

        if (id && !mapById.has(id)) mapById.set(id, d);

      });

      return Array.from(mapById.values());

    }


    function getZeroWasteStockKeywords() {

      const raw = getElVal("input-stock", "").toLowerCase();

      if (!raw.trim()) return [];

      return raw

        .split(/[,;\n]+/)

        .map(part => part.replace(/\d+([.,]\d+)?\s*(kg|g|l|ml|szt|op)?/gi, "").trim())

        .filter(tok => tok.length >= 3);

    }


    function getMatchedStockBadges(dishObj, stockTokens) {

      if (!dishObj || !stockTokens || !stockTokens.length) return "";

      const haystack = [

        dishObj.title || "",

        dishObj.base || "",

        dishObj.side || "",

        dishObj.salad || "",

        ...(dishObj.ingredients || []).map(i => i.name || "")

      ].join(" ").toLowerCase();


      const matched = stockTokens.filter(tok => {

        const stem = tok.slice(0, Math.max(4, tok.length - 1));

        return haystack.includes(tok) || haystack.includes(stem);

      });

      return matched.slice(0, 2).join(", ");

    }


    function getRegularMenuBlockedTerms() {

      const raw = getElVal("input-regular-menu", "").toLowerCase();

      if (!raw.trim()) return [];

      return raw.split(/[,;\n]+/).map(s => s.trim()).filter(s => s.length >= 3);

    }


    function isDishBlockedByRegularMenu(dishObj, regularTerms) {

      if (!regularTerms || !regularTerms.length) return false;

      const text = ((dishObj.title || dishObj.base || "") + " " + (dishObj.side || "")).toLowerCase();

      return regularTerms.some(term => text.includes(term));

    }


    function getRecentBlockedDishNames() {

      const blocked = new Set();

      const now = Date.now();

      const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;

      (appState.historyLog || []).forEach(entry => {

        if (!entry || !entry.savedAt) return;

        if (now - entry.savedAt <= thirtyDaysMs) {

          (entry.dishes || []).forEach(name => {

            if (name) blocked.add(String(name).toLowerCase().trim());

          });

        }

      });

      return blocked;

    }


    function scoreCandidate(dishObj, stockTokens, selectedSeason) {

      let score = 0;

      if (stockTokens && stockTokens.length) {

        const matched = getMatchedStockBadges(dishObj, stockTokens);

        if (matched) score += 10;

      }

      if (dishObj.seasons && dishObj.seasons.includes(selectedSeason)) {

        score += 3;

      }

      const targetFc = parseFloat(getElVal("input-target-fc", "13.50")) || 13.50;

      const cost = computeDishCost(dishObj);

      if (cost <= targetFc * 0.55) score += 1.5;

      return score;

    }


    // =========================================================================

    // 6. GEMINI AI-CHEF INTEGRATION (AUTO-RENEWING RECIPES & EVENT MENU)

    // =========================================================================

    async function callGeminiJson(promptText, schemaObj) {

      const apiKey = "";

      const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent?key=${apiKey}`;

      const payload = {

        contents: [{ parts: [{ text: promptText }] }],

        generationConfig: {

          responseMimeType: "application/json",

          responseSchema: schemaObj

        }

      };


      const resp = await fetchWithRetry(apiUrl, {

        method: "POST",

        headers: { "Content-Type": "application/json" },

        body: JSON.stringify(payload)

      });

      if (!resp || !resp.ok) return null;

      const data = await resp.json();

      const rawText = data?.candidates?.[0]?.content?.parts?.[0]?.text;

      if (!rawText) return null;

      return JSON.parse(rawText);

    }


    async function autoExpandDishDatabase(showFeedback = true) {

      const banner = document.getElementById("ai-loading-banner");

      if (showFeedback && banner) banner.classList.remove("hidden");


      const season = getElVal("input-season", "autumn");

      const stock = getElVal("input-stock", "");

      const regular = getElVal("input-regular-menu", "");

      const existingNames = [

        ...buildSlotPool("meatSoup"),

        ...buildSlotPool("vegSoup"),

        ...buildSlotPool("meatMain"),

        ...buildSlotPool("vegMain")

      ].map(d => d.title || d.base).slice(0, 45).join("; ");


      const prompt = `Jesteś szefem kuchni B2B. Wygeneruj 4 nowe, smaczne, opłacalne dania lunchowe (po 1 dla: meatSoup, vegSoup, meatMain, vegMain) w języku "${appState.lang}".

Zasady:

1. Sezon: ${season}. Magazyn Zero-Waste do priorytetowego zużycia: "${stock || "brak"}".

2. NIE powtarzaj dań ze stałej karty: "${regular || "brak"}" ani tych istniejących: "${existingNames}".

3. Nazwy zup czyste (bez pisania na jakim są wywarze), chipsy warzywne TYLKO do zup typu krem.

4. Jeśli coś się piecze lub gotuje sous-vide, podaj temperaturę i czas w polu tech.

5. Podaj komplet składników z gramaturą brutto na 1 porcję, odpadem %, jednostką (g lub ml) i ceną za 1g/1ml w PLN (np. 0.025 dla 25 zł/kg).`;


      const schema = {

        type: "OBJECT",

        properties: {

          items: {

            type: "ARRAY",

            items: {

              type: "OBJECT",

              properties: {

                slot: { type: "STRING" },

                titleOrBase: { type: "STRING" },

                side: { type: "STRING" },

                salad: { type: "STRING" },

                yield: { type: "STRING" },

                tech: { type: "STRING" },

                ingredients: {

                  type: "ARRAY",

                  items: {

                    type: "OBJECT",

                    properties: {

                      name: { type: "STRING" },

                      cat: { type: "STRING" },

                      gross: { type: "NUMBER" },

                      waste: { type: "NUMBER" },

                      unit: { type: "STRING" },

                      pricePerUnit: { type: "NUMBER" }

                    },

                    required: ["name", "cat", "gross", "waste", "unit", "pricePerUnit"]

                  }

                }

              },

              required: ["slot", "titleOrBase", "yield", "tech", "ingredients"]

            }

          }

        },

        required: ["items"]

      };


      try {

        const result = await callGeminiJson(prompt, schema);

        if (result && Array.isArray(result.items)) {

          const metaScale = (appState.lang === "pl") ? 1 : ((COUNTRY_META[appState.lang]?.scale) || 0.25);

          let added = 0;

          result.items.forEach(item => {

            const slot = ["meatSoup", "vegSoup", "meatMain", "vegMain"].includes(item.slot) ? item.slot : null;

            if (!slot || !item.titleOrBase) return;

            const isSoup = slot === "meatSoup" || slot === "vegSoup";

            const formatted = {

              yield: item.yield || (isSoup ? "300 ml" : "430 g"),

              seasons: [season],

              tech: item.tech || "Obróbka termiczna zgodnie ze standardem kuchni.",

              ingredients: (item.ingredients || []).map(ing => {

                const gross = Math.max(1, Number(ing.gross) || 50);

                const waste = Math.min(80, Math.max(0, Number(ing.waste) || 0));

                return {

                  name: ing.name,

                  cat: ["meat", "veg", "dairy", "dry", "liquids", "spices"].includes(ing.cat) ? ing.cat : "veg",

                  gross,

                  waste,

                  net: Math.round(gross * (1 - waste / 100) * 10) / 10,

                  unit: ing.unit === "ml" ? "ml" : "g",

                  pricePerUnit: +((Number(ing.pricePerUnit) || 0.01) * metaScale).toFixed(4)

                };

              })

            };

            if (isSoup) {

              formatted.title = item.titleOrBase;

            } else {

              formatted.base = item.titleOrBase;

              formatted.side = item.side || "Ziemniaki gotowane z wody z koperkiem";

              formatted.salad = item.salad || "Surówka sezonowa";

              verifyAndHarmonizeDish(formatted, false);

            }

            appState.aiCustomPool[slot].push(formatted);

            added++;

          });


          const totalCount = ["meatSoup", "vegSoup", "meatMain", "vegMain"].reduce((acc, s) => acc + buildSlotPool(s).length, 0);

          const badge = document.getElementById("auto-db-badge");

          if (badge) badge.textContent = `🔄 Baza auto-odnawiana: ${totalCount * 8}+ kombinacji`;

          savePersistentState();

          if (showFeedback) {

            showToast(added > 0 ? `✨ AI-Chef dodał ${added} nowe autorskie receptury do bazy!` : "✨ Baza receptur jest aktualna i gotowa!");

          }

        }

      } catch (e) {

        if (showFeedback) showToast("✨ Użyto rozszerzonej bazy lokalnej Szefa Kuchni.");

      } finally {

        if (banner) banner.classList.add("hidden");

      }

    }


    // =========================================================================

    // 7. CALCULATIONS, TTK, PROCUREMENT & UI RENDERING

    // =========================================================================

    function computeDishCost(dishObj) {

      if (!dishObj || !Array.isArray(dishObj.ingredients)) return 0;

      return dishObj.ingredients.reduce((sum, ing) => {

        return sum + (Number(ing.gross) || 0) * (Number(ing.pricePerUnit) || 0);

      }, 0);

    }


    function detectAllergensForDish(dishObj) {

      const txt = [

        dishObj.title || "",

        dishObj.base || "",

        dishObj.side || "",

        dishObj.salad || "",

        ...(dishObj.ingredients || []).map(i => i.name || "")

      ].join(" ").toLowerCase();


      const found = [];

      if (/mąk|makaron|penne|lasagne|pierog|kopytk|pęczak|jęczmien|bulgur|panko|naleśnik|zakwas/.test(txt)) found.push("Gluten");

      if (/śmietan|masł|ser|twaróg|parmezan|mozzarella|ricotta|kefir|mlek/.test(txt)) found.push("Laktoza");

      if (/jajk|jajecz/.test(txt)) found.push("Jaja");

      if (/dorsz|łosoś|ryb|sandacz/.test(txt)) found.push("Ryby");

      if (/orzech|migdał/.test(txt)) found.push("Orzechy");

      if (/seler|włoszczyzn/.test(txt)) found.push("Seler");

      if (/musztard|gorczyc/.test(txt)) found.push("Gorczyca");

      if (/sezam/.test(txt)) found.push("Sezam");

      return found;

    }


    function recalculateAll() {

      const monThu = Math.max(1, parseInt(getElVal("input-portions-mon-thu", "50")) || 50);

      const fri = Math.max(1, parseInt(getElVal("input-portions-fri", "70")) || 70);

      const vegShare = (parseInt(getElVal("input-veg-share", "30")) || 30) / 100;

      const targetFc = parseFloat(getElVal("input-target-fc", "13.50")) || 13.50;

      const guestPrice = parseFloat(getElVal("input-guest-price", "39")) || 39;


      let totalMeatSetCost = 0;

      let totalVegSetCost = 0;

      let weeklyProcurementCost = 0;

      let totalPortionsWeek = 0;


      appState.profile.days.forEach((day, idx) => {

        const dayPortions = idx === 4 ? fri : monThu;

        const vegPortions = Math.round(dayPortions * vegShare);

        const meatPortions = Math.max(0, dayPortions - vegPortions);


        const meatSet = computeDishCost(day.meatSoup) + computeDishCost(day.meatMain);

        const vegSet = computeDishCost(day.vegSoup) + computeDishCost(day.vegMain);


        totalMeatSetCost += meatSet;

        totalVegSetCost += vegSet;

        totalPortionsWeek += dayPortions;

        weeklyProcurementCost += (meatSet * meatPortions) + (vegSet * vegPortions);

      });


      const avgMeat = totalMeatSetCost / 5;

      const avgVeg = totalVegSetCost / 5;

      const fcPercent = guestPrice > 0 ? ((avgMeat / guestPrice) * 100).toFixed(1) : "0.0";

      const grossProfitWeek = (totalPortionsWeek * guestPrice) - weeklyProcurementCost;


      const avgFcEl = document.getElementById("kpi-avg-fc");

      if (avgFcEl) avgFcEl.textContent = formatMoney(avgMeat);


      const fcBadge = document.getElementById("kpi-fc-badge");

      if (fcBadge) {

        const isOk = avgMeat <= targetFc;

        fcBadge.textContent = `${fcPercent}% • ${isOk ? "OK" : "HIGH"}`;

        fcBadge.className = `text-[11px] font-semibold px-2 py-0.5 rounded-full ${isOk ? "bg-emerald-100 text-emerald-700" : "bg-rose-100 text-rose-700"}`;

      }


      const vegFcEl = document.getElementById("kpi-veg-fc");

      if (vegFcEl) vegFcEl.textContent = formatMoney(avgVeg);


      const totPorEl = document.getElementById("kpi-total-portions");

      if (totPorEl) totPorEl.textContent = String(totalPortionsWeek);


      const splitEl = document.getElementById("kpi-portions-split");

      if (splitEl) splitEl.textContent = `${Math.round((1 - vegShare) * 100)}% 🍖 / ${Math.round(vegShare * 100)}% 🌱`;


      const budEl = document.getElementById("kpi-weekly-budget");

      if (budEl) budEl.textContent = formatMoney(weeklyProcurementCost);


      const profEl = document.getElementById("kpi-profit-badge");

      if (profEl) profEl.textContent = `+${formatMoney(grossProfitWeek)}`;


      const blockedCount = getRecentBlockedDishNames().size;

      const antiRep = document.getElementById("anti-repeat-badge");

      if (antiRep) antiRep.textContent = t("anti_repeat_tpl").replace("{n}", blockedCount);


      renderWeeklyMenuGrid();

      renderTtkView();

      renderProcurementView();

      renderPrintAndSmmView();

      renderHistoryView();

      savePersistentState();

    }


    function renderWeeklyMenuGrid() {

      const grid = document.getElementById("weekly-menu-grid");

      if (!grid) return;

      const dict = I18N[appState.lang] || I18N.pl;

      const mondayIso = getElVal("input-week-start", appState.weekStartDate);

      const monThuPortions = parseInt(getElVal("input-portions-mon-thu", "50")) || 50;

      const friPortions = parseInt(getElVal("input-portions-fri", "70")) || 70;

      const stockTokens = getZeroWasteStockKeywords();


      grid.innerHTML = appState.profile.days.map((day, dIdx) => {

        const dateStr = addDaysToIso(mondayIso, dIdx);

        const portions = dIdx === 4 ? friPortions : monThuPortions;

        const meatSetCost = computeDishCost(day.meatSoup) + computeDishCost(day.meatMain);

        const vegSetCost = computeDishCost(day.vegSoup) + computeDishCost(day.vegMain);


        const zwMeatSoup = getMatchedStockBadges(day.meatSoup, stockTokens);

        const zwVegSoup = getMatchedStockBadges(day.vegSoup, stockTokens);

        const zwMeatMain = getMatchedStockBadges(day.meatMain, stockTokens);

        const zwVegMain = getMatchedStockBadges(day.vegMain, stockTokens);


        return `

          <div class="bg-white rounded-2xl border border-slate-200/90 shadow-sm overflow-hidden">

            <div class="bg-slate-900 text-white px-4 sm:px-6 py-3 flex flex-wrap items-center justify-between gap-2">

              <div class="flex items-center gap-2.5">

                <span class="font-bold text-sm sm:text-base">${escapeHtml(dict.days[dIdx])}</span>

                <span class="text-xs text-slate-400 bg-slate-800 px-2.5 py-0.5 rounded-full">${escapeHtml(dateStr)}</span>

                <span class="text-xs font-semibold text-emerald-300 bg-emerald-950/80 border border-emerald-700/60 px-2.5 py-0.5 rounded-full">${portions} ${escapeHtml(dict.portions_unit)}</span>

              </div>

              <div class="flex items-center gap-3 text-xs">

                <span class="text-slate-300">🍖 FC: <strong class="text-white">${formatMoney(meatSetCost)}</strong></span>

                <span class="text-slate-300">🌱 FC: <strong class="text-emerald-300">${formatMoney(vegSetCost)}</strong></span>

                <button onclick="openTtkForDay(${dIdx})" class="px-2.5 py-1 rounded-lg bg-slate-800 hover:bg-slate-700 text-white font-semibold border border-slate-700 cursor-pointer">

                  📋 ${escapeHtml(t("btn_ttk"))}

                </button>

              </div>

            </div>


            <div class="p-4 sm:p-6 grid grid-cols-1 lg:grid-cols-2 gap-5">

              <!-- 1. SOUPS -->

              <div class="space-y-3">

                <div class="text-xs font-extrabold uppercase tracking-wider text-slate-400">${escapeHtml(t("sec_soups"))}</div>


                <div class="rounded-xl border border-slate-200 bg-slate-50/70 p-3.5 space-y-2">

                  <div class="flex items-center justify-between gap-2">

                    <div class="flex flex-wrap items-center gap-1.5">

                      <span class="text-[11px] font-bold px-2 py-0.5 rounded-md bg-rose-100 text-rose-800">${escapeHtml(t("badge_meat_soup"))}</span>

                      ${zwMeatSoup ? `<span class="text-[11px] font-bold px-2 py-0.5 rounded-md bg-amber-100 text-amber-900 border border-amber-300">♻️ ${escapeHtml(zwMeatSoup)}</span>` : ""}

                    </div>

                    <div class="flex items-center gap-1.5">

                      <span class="text-xs font-bold text-slate-700">${formatMoney(computeDishCost(day.meatSoup))}</span>

                      <button onclick="swapDish(${dIdx}, 'meatSoup')" class="px-2.5 py-1 rounded-lg bg-white hover:bg-slate-100 border border-slate-300 text-xs font-semibold text-slate-700 cursor-pointer">

                        🔄 ${escapeHtml(t("btn_swap"))}

                      </button>

                    </div>

                  </div>

                  <div onclick="openDishEditModal(${dIdx}, 'meatSoup')" class="font-semibold text-sm text-slate-900 hover:text-emerald-700 cursor-pointer transition-colors" title="Kliknij, aby edytować nazwę lub recepturę">

                    ${escapeHtml(day.meatSoup.title)} <span class="text-xs text-slate-400">✏️</span>

                  </div>

                </div>


                <div class="rounded-xl border border-emerald-200/80 bg-emerald-50/40 p-3.5 space-y-2">

                  <div class="flex items-center justify-between gap-2">

                    <div class="flex flex-wrap items-center gap-1.5">

                      <span class="text-[11px] font-bold px-2 py-0.5 rounded-md bg-emerald-100 text-emerald-800">${escapeHtml(t("badge_veg_soup"))}</span>

                      ${zwVegSoup ? `<span class="text-[11px] font-bold px-2 py-0.5 rounded-md bg-amber-100 text-amber-900 border border-amber-300">♻️ ${escapeHtml(zwVegSoup)}</span>` : ""}

                    </div>

                    <div class="flex items-center gap-1.5">

                      <span class="text-xs font-bold text-emerald-800">${formatMoney(computeDishCost(day.vegSoup))}</span>

                      <button onclick="swapDish(${dIdx}, 'vegSoup')" class="px-2.5 py-1 rounded-lg bg-white hover:bg-emerald-50 border border-emerald-300 text-xs font-semibold text-emerald-800 cursor-pointer">

                        🔄 ${escapeHtml(t("btn_swap"))}

                      </button>

                    </div>

                  </div>

                  <div onclick="openDishEditModal(${dIdx}, 'vegSoup')" class="font-semibold text-sm text-slate-900 hover:text-emerald-700 cursor-pointer transition-colors" title="Kliknij, aby edytować nazwę lub recepturę">

                    ${escapeHtml(day.vegSoup.title)} <span class="text-xs text-slate-400">✏️</span>

                  </div>

                </div>

              </div>


              <!-- 2. MAIN COURSES -->

              <div class="space-y-3">

                <div class="text-xs font-extrabold uppercase tracking-wider text-slate-400">${escapeHtml(t("sec_mains"))}</div>


                <div class="rounded-xl border border-slate-200 bg-slate-50/70 p-3.5 space-y-2">

                  <div class="flex items-center justify-between gap-2">

                    <div class="flex flex-wrap items-center gap-1.5">

                      <span class="text-[11px] font-bold px-2 py-0.5 rounded-md bg-amber-100 text-amber-900">${escapeHtml(t("badge_meat_main"))}</span>

                      ${zwMeatMain ? `<span class="text-[11px] font-bold px-2 py-0.5 rounded-md bg-amber-200 text-amber-950 border border-amber-400">♻️ ${escapeHtml(zwMeatMain)}</span>` : ""}

                    </div>

                    <div class="flex items-center gap-1.5">

                      <span class="text-xs font-bold text-slate-700">${formatMoney(computeDishCost(day.meatMain))}</span>

                      <button onclick="swapDish(${dIdx}, 'meatMain')" class="px-2.5 py-1 rounded-lg bg-white hover:bg-slate-100 border border-slate-300 text-xs font-semibold text-slate-700 cursor-pointer">

                        🔄 ${escapeHtml(t("btn_swap"))}

                      </button>

                    </div>

                  </div>

                  <div onclick="openDishEditModal(${dIdx}, 'meatMain')" class="font-semibold text-sm text-slate-900 hover:text-emerald-700 cursor-pointer transition-colors" title="Kliknij, aby edytować danie i dodatki">

                    ${escapeHtml(day.meatMain.base)} <span class="text-xs text-slate-400">✏️</span>

                  </div>

                  <div class="flex flex-wrap items-center gap-1.5 pt-1 text-xs">

                    <button onclick="cycleStarchSide(${dIdx}, 'meatMain')" class="px-2 py-1 rounded-lg bg-white hover:bg-amber-50 border border-slate-200 text-slate-700 font-medium cursor-pointer" title="Zmień dodatek">

                      🥔 ${escapeHtml(day.meatMain.side)}

                    </button>

                    <button onclick="cycleSaladSide(${dIdx}, 'meatMain')" class="px-2 py-1 rounded-lg bg-white hover:bg-emerald-50 border border-slate-200 text-slate-700 font-medium cursor-pointer" title="Zmień surówkę / warzywa">

                      🥗 ${escapeHtml(day.meatMain.salad)}

                    </button>

                  </div>

                </div>


                <div class="rounded-xl border border-emerald-200/80 bg-emerald-50/40 p-3.5 space-y-2">

                  <div class="flex items-center justify-between gap-2">

                    <div class="flex flex-wrap items-center gap-1.5">

                      <span class="text-[11px] font-bold px-2 py-0.5 rounded-md bg-emerald-100 text-emerald-800">${escapeHtml(t("badge_veg_main"))}</span>

                      ${zwVegMain ? `<span class="text-[11px] font-bold px-2 py-0.5 rounded-md bg-amber-100 text-amber-900 border border-amber-300">♻️ ${escapeHtml(zwVegMain)}</span>` : ""}

                    </div>

                    <div class="flex items-center gap-1.5">

                      <span class="text-xs font-bold text-emerald-800">${formatMoney(computeDishCost(day.vegMain))}</span>

                      <button onclick="swapDish(${dIdx}, 'vegMain')" class="px-2.5 py-1 rounded-lg bg-white hover:bg-emerald-50 border border-emerald-300 text-xs font-semibold text-emerald-800 cursor-pointer">

                        🔄 ${escapeHtml(t("btn_swap"))}

                      </button>

                    </div>

                  </div>

                  <div onclick="openDishEditModal(${dIdx}, 'vegMain')" class="font-semibold text-sm text-slate-900 hover:text-emerald-700 cursor-pointer transition-colors" title="Kliknij, aby edytować danie i dodatki">

                    ${escapeHtml(day.vegMain.base)} <span class="text-xs text-slate-400">✏️</span>

                  </div>

                  <div class="flex flex-wrap items-center gap-1.5 pt-1 text-xs">

                    <button onclick="cycleStarchSide(${dIdx}, 'vegMain')" class="px-2 py-1 rounded-lg bg-white hover:bg-amber-50 border border-emerald-200 text-slate-700 font-medium cursor-pointer" title="Zmień dodatek">

                      🥔 ${escapeHtml(day.vegMain.side)}

                    </button>

                    <button onclick="cycleSaladSide(${dIdx}, 'vegMain')" class="px-2 py-1 rounded-lg bg-white hover:bg-emerald-50 border border-emerald-200 text-slate-700 font-medium cursor-pointer" title="Zmień surówkę / warzywa">

                      🥗 ${escapeHtml(day.vegMain.salad)}

                    </button>

                  </div>

                </div>

              </div>

            </div>

          </div>

        `;

      }).join("");

    }


    function getDishIdentifier(dishObj) {

      if (!dishObj) return "";

      return String(dishObj.title || dishObj.base || "").toLowerCase().trim();

    }


    function enforceWeeklyUniqueness(priorityDayIdx = -1) {

      let replacedCount = 0;

      const order = [];

      if (priorityDayIdx >= 0 && priorityDayIdx < 5) order.push(priorityDayIdx);

      for (let i = 0; i < 5; i++) {

        if (!order.includes(i)) order.push(i);

      }


      const seenSoups = new Set();

      const seenMeatMains = new Set();

      const seenVegMains = new Set();

      const blockedHistory = getRecentBlockedDishNames();

      const regularTerms = getRegularMenuBlockedTerms();

      const stockTokens = getZeroWasteStockKeywords();

      const selectedSeason = getElVal("input-season", "autumn");


      for (const dIdx of order) {

        const day = appState.profile.days[dIdx];

        if (!day) continue;


        ["meatSoup", "vegSoup"].forEach(slotKey => {

          const id = getDishIdentifier(day[slotKey]);

          if (seenSoups.has(id)) {

            const pool = buildSlotPool(slotKey);

            let candidates = pool.filter(c => {

              const cid = getDishIdentifier(c);

              return !seenSoups.has(cid) && !blockedHistory.has(cid) && !isDishBlockedByRegularMenu(c, regularTerms);

            });

            if (!candidates.length) candidates = pool.filter(c => !seenSoups.has(getDishIdentifier(c)));

            if (candidates.length) {

              candidates.sort((a, b) => scoreCandidate(b, stockTokens, selectedSeason) - scoreCandidate(a, stockTokens, selectedSeason));

              day[slotKey] = JSON.parse(JSON.stringify(candidates[0]));

              replacedCount++;

            }

          }

          seenSoups.add(getDishIdentifier(day[slotKey]));

        });


        [

          { slotKey: "meatMain", seenSet: seenMeatMains },

          { slotKey: "vegMain", seenSet: seenVegMains }

        ].forEach(({ slotKey, seenSet }) => {

          const id = getDishIdentifier(day[slotKey]);

          if (seenSet.has(id)) {

            const pool = buildSlotPool(slotKey);

            let candidates = pool.filter(c => {

              const cid = getDishIdentifier(c);

              return !seenSet.has(cid) && !blockedHistory.has(cid) && !isDishBlockedByRegularMenu(c, regularTerms);

            });

            if (!candidates.length) candidates = pool.filter(c => !seenSet.has(getDishIdentifier(c)));

            if (candidates.length) {

              candidates.sort((a, b) => scoreCandidate(b, stockTokens, selectedSeason) - scoreCandidate(a, stockTokens, selectedSeason));

              const picked = JSON.parse(JSON.stringify(candidates[0]));

              verifyAndHarmonizeDish(picked, true);

              day[slotKey] = picked;

              replacedCount++;

            }

          }

          seenSet.add(getDishIdentifier(day[slotKey]));

        });

      }

      return replacedCount;

    }


    async function fetchWithRetry(url, options, maxRetries = 4) {

      let delay = 800;

      for (let attempt = 0; attempt <= maxRetries; attempt++) {

        try {

          const response = await fetch(url, options);

          if (response.ok) return response;

          if (response.status === 429 || response.status >= 500) {

            if (attempt === maxRetries) return response;

          } else {

            return response;

          }

        } catch (err) {

          if (attempt === maxRetries) throw err;

        }

        await new Promise(res => setTimeout(res, delay));

        delay *= 2;

      }

      return null;

    }


    let _editingDishTarget = null;


    function openDishEditModal(dayIdx, slotKey) {

      _editingDishTarget = { dayIdx, slotKey };

      const dish = appState.profile.days[dayIdx][slotKey];

      const isSoup = slotKey === "meatSoup" || slotKey === "vegSoup";


      setElVal("dish-edit-title", isSoup ? (dish.title || "") : (dish.base || ""));

      setElVal("dish-edit-tech", dish.tech || "");


      const sidesWrap = document.getElementById("dish-edit-sides-wrap");

      if (sidesWrap) {

        if (isSoup) {

          sidesWrap.classList.add("hidden");

        } else {

          sidesWrap.classList.remove("hidden");

          setElVal("dish-edit-side", dish.side || "");

          setElVal("dish-edit-salad", dish.salad || "");


          const starchList = document.getElementById("starch-suggestions");

          if (starchList) {

            starchList.innerHTML = [...STARCH_SIDE_POOL.pl, ...GARNISH_ONLY_TOPPINGS.pl]

              .map(s => `<option value="${escapeHtml(s.name)}"></option>`).join("");

          }


          const saladList = document.getElementById("salad-suggestions");

          if (saladList) {

            saladList.innerHTML = [...SALAD_SIDE_POOL.pl, ...LIGHT_SALAD_ONLY.pl]

              .map(s => `<option value="${escapeHtml(s)}"></option>`).join("");

          }

        }

      }


      const modal = document.getElementById("dish-edit-modal");

      if (modal) modal.classList.remove("hidden");

    }


    function closeDishEditModal() {

      const modal = document.getElementById("dish-edit-modal");

      if (modal) modal.classList.add("hidden");

    }


    function openTtkFromDishModal() {

      if (!_editingDishTarget) return;

      const dayIdx = _editingDishTarget.dayIdx;

      closeDishEditModal();

      openTtkForDay(dayIdx);

    }


    function confirmDishEdit() {

      if (!_editingDishTarget) return;

      const { dayIdx, slotKey } = _editingDishTarget;

      const dish = appState.profile.days[dayIdx][slotKey];

      const isSoup = slotKey === "meatSoup" || slotKey === "vegSoup";


      const newTitle = getElVal("dish-edit-title", "").trim();

      const newTech = getElVal("dish-edit-tech", "").trim();


      if (newTitle) {

        if (isSoup) dish.title = newTitle;

        else dish.base = newTitle;

      }

      if (newTech) dish.tech = newTech;


      if (!isSoup) {

        const newSide = getElVal("dish-edit-side", "").trim();

        const newSalad = getElVal("dish-edit-salad", "").trim();

        dish.side = newSide;

        dish.salad = newSalad;

        const inferredStarch = inferStarchIngredientFromText(newSide);

        syncMainSideIngredients(dish, inferredStarch, newSalad);

      }


      closeDishEditModal();

      const replacedCount = enforceWeeklyUniqueness(dayIdx);

      recalculateAll();

      if (replacedCount > 0) {

        showToast(`✅ Zaktualizowano danie i automatycznie wymieniono ${replacedCount} powtarzającą się pozycję w dalszych dniach tygodnia!`);

      } else {

        showToast("✅ Zaktualizowano danie, dodatki oraz kartę TTK!");

      }

    }


    function cycleStarchSide(dayIdx, slotKey) {

      const dish = appState.profile.days[dayIdx][slotKey];

      const compat = getCompatibleSidesForDish(dish.base);

      const list = compat.starches;

      const curIdx = list.findIndex(s => s.name === dish.side);

      const next = list[(curIdx + 1) % list.length];

      dish.side = next.name;

      syncMainSideIngredients(dish, next, dish.salad);

      recalculateAll();

      showToast(`🥔 ${next.name}`);

    }


    function cycleSaladSide(dayIdx, slotKey) {

      const dish = appState.profile.days[dayIdx][slotKey];

      const compat = getCompatibleSidesForDish(dish.base);

      const list = compat.salads;

      const curIdx = list.findIndex(s => s === dish.salad);

      const next = list[(curIdx + 1) % list.length];

      dish.salad = next;

      const inferredStarch = inferStarchIngredientFromText(dish.side);

      syncMainSideIngredients(dish, inferredStarch, next);

      recalculateAll();

      showToast(`🥗 ${next}`);

    }


    let _editingPriceTarget = null;


    function openIngredientEditor(dayIdx, slotKey, ingIdx) {

      _editingPriceTarget = { dayIdx, slotKey, ingIdx };

      const dish = appState.profile.days[dayIdx][slotKey];

      const isNew = ingIdx === -1;

      const ing = isNew

        ? { name: "Nowy składnik", gross: 30, waste: 0, pricePerUnit: 0.02 }

        : dish.ingredients[ingIdx];


      setElVal("ing-edit-name", ing.name);

      setElVal("ing-edit-gross", ing.gross);

      setElVal("ing-edit-waste", ing.waste || 0);

      setElVal("price-edit-input", +(ing.pricePerUnit * 1000).toFixed(2));


      const delBtn = document.getElementById("btn-delete-ing");

      if (delBtn) delBtn.style.display = isNew ? "none" : "inline-block";


      const modal = document.getElementById("price-edit-modal");

      if (modal) modal.classList.remove("hidden");

    }


    function closePriceEditModal() {

      const modal = document.getElementById("price-edit-modal");

      if (modal) modal.classList.add("hidden");

    }


    function confirmIngredientPriceEdit() {

      if (!_editingPriceTarget) return;

      const { dayIdx, slotKey, ingIdx } = _editingPriceTarget;

      const dish = appState.profile.days[dayIdx][slotKey];


      const name = getElVal("ing-edit-name", "Składnik").trim() || "Składnik";

      const gross = Math.max(1, parseFloat(getElVal("ing-edit-gross", "50")) || 50);

      const waste = Math.min(80, Math.max(0, parseFloat(getElVal("ing-edit-waste", "0")) || 0));

      const pricePerKg = Math.max(0, parseFloat(getElVal("price-edit-input", "10")) || 0);

      const net = Math.round(gross * (1 - waste / 100) * 10) / 10;


      if (ingIdx === -1) {

        dish.ingredients.push({

          name,

          cat: "veg",

          gross,

          waste,

          net,

          unit: "g",

          pricePerUnit: +(pricePerKg / 1000).toFixed(5)

        });

      } else {

        const target = dish.ingredients[ingIdx];

        target.name = name;

        target.gross = gross;

        target.waste = waste;

        target.net = net;

        target.pricePerUnit = +(pricePerKg / 1000).toFixed(5);

      }


      closePriceEditModal();

      recalculateAll();

      showToast("✅ Zaktualizowano składnik i przeliczono recepturę!");

    }


    function deleteIngredientFromTtk() {

      if (!_editingPriceTarget || _editingPriceTarget.ingIdx < 0) return;

      const { dayIdx, slotKey, ingIdx } = _editingPriceTarget;

      appState.profile.days[dayIdx][slotKey].ingredients.splice(ingIdx, 1);

      closePriceEditModal();

      recalculateAll();

      showToast("🗑️ Usunięto składnik z karty TTK");

    }


    // =========================================================================

    // 9. TTK, PROCUREMENT, PRINT/SMM, EVENTS & HISTORY VIEWS

    // =========================================================================

    function setTtkMode(mode) {

      appState.ttkMode = mode;

      const btnS = document.getElementById("btn-ttk-single");

      const btnB = document.getElementById("btn-ttk-batch");

      if (btnS && btnB) {

        btnS.className = mode === "single" ? "px-3.5 py-1.5 rounded-lg text-xs font-semibold bg-white text-slate-900 shadow-sm cursor-pointer" : "px-3.5 py-1.5 rounded-lg text-xs font-semibold text-slate-600 cursor-pointer";

        btnB.className = mode === "batch" ? "px-3.5 py-1.5 rounded-lg text-xs font-semibold bg-white text-slate-900 shadow-sm cursor-pointer" : "px-3.5 py-1.5 rounded-lg text-xs font-semibold text-slate-600 cursor-pointer";

      }

      renderTtkView();

    }


    function openTtkForDay(dayIdx) {

      appState.ttkSelectedDay = dayIdx;

      switchTab("ttk");

    }


    function renderTtkView() {

      const pills = document.getElementById("ttk-day-pills");

      const container = document.getElementById("ttk-cards-container");

      if (!pills || !container) return;


      const dict = I18N[appState.lang] || I18N.pl;

      pills.innerHTML = dict.days.map((d, i) => `

        <button onclick="appState.ttkSelectedDay=${i}; renderTtkView()" class="px-3.5 py-1.5 rounded-xl text-xs font-semibold transition-all cursor-pointer ${appState.ttkSelectedDay === i ? "bg-slate-900 text-white shadow-sm" : "bg-slate-100 text-slate-700 hover:bg-slate-200"}">

          ${escapeHtml(d)}

        </button>

      `).join("");


      const dayIdx = appState.ttkSelectedDay;

      const day = appState.profile.days[dayIdx];

      const monThu = parseInt(getElVal("input-portions-mon-thu", "50")) || 50;

      const fri = parseInt(getElVal("input-portions-fri", "70")) || 70;

      const vegShare = (parseInt(getElVal("input-veg-share", "30")) || 30) / 100;

      const dayTotal = dayIdx === 4 ? fri : monThu;

      const vegPortions = Math.max(1, Math.round(dayTotal * vegShare));

      const meatPortions = Math.max(1, dayTotal - vegPortions);


      const slots = [

        { key: "meatSoup", label: t("badge_meat_soup"), portions: meatPortions },

        { key: "vegSoup", label: t("badge_veg_soup"), portions: vegPortions },

        { key: "meatMain", label: t("badge_meat_main"), portions: meatPortions },

        { key: "vegMain", label: t("badge_veg_main"), portions: vegPortions }

      ];


      container.innerHTML = slots.map(s => {

        const dish = day[s.key];

        const unitCost = computeDishCost(dish);

        const batchCost = unitCost * s.portions;

        const mult = appState.ttkMode === "batch" ? s.portions : 1;

        const title = dish.title || `${dish.base} + ${dish.side}`;


        return `

          <div class="bg-white rounded-2xl border border-slate-200 shadow-sm p-5 flex flex-col justify-between space-y-4">

            <div>

              <div class="flex items-center justify-between gap-2 mb-2">

                <span class="text-[11px] font-bold px-2.5 py-0.5 rounded-md bg-slate-100 text-slate-800">${escapeHtml(s.label)}</span>

                <span class="text-xs font-semibold text-slate-500">${escapeHtml(t("lbl_yield"))}: <strong class="text-slate-800">${escapeHtml(dish.yield || "350 g")}</strong></span>

              </div>

              <h3 class="text-sm sm:text-base font-bold text-slate-900">${escapeHtml(title)}</h3>

              ${dish.salad ? `<div class="text-xs text-emerald-700 font-medium mt-0.5">🥗 ${escapeHtml(dish.salad)}</div>` : ""}


              <div class="mt-3 overflow-x-auto">

                <table class="w-full text-xs text-left">

                  <thead class="text-[11px] uppercase text-slate-400 border-b border-slate-100">

                    <tr>

                      <th class="py-2">${escapeHtml(t("th_ing"))}</th>

                      <th class="py-2 text-right">${escapeHtml(t("th_gross"))}</th>

                      <th class="py-2 text-right">${escapeHtml(t("th_waste"))}</th>

                      <th class="py-2 text-right">${escapeHtml(t("th_net"))}</th>

                      <th class="py-2 text-right">${escapeHtml(t("th_cost"))}</th>

                    </tr>

                  </thead>

                  <tbody class="divide-y divide-slate-100">

                    ${dish.ingredients.map((ing, iIdx) => {

                      const gVal = ing.gross * mult;

                      const nVal = (ing.net || ing.gross) * mult;

                      const cVal = ing.gross * ing.pricePerUnit * mult;

                      const fmtQty = appState.ttkMode === "batch"

                        ? `${(gVal / 1000).toFixed(2)} ${ing.unit === "ml" ? "l" : "kg"}`

                        : `${Math.round(gVal)} ${ing.unit}`;

                      const fmtNet = appState.ttkMode === "batch"

                        ? `${(nVal / 1000).toFixed(2)} ${ing.unit === "ml" ? "l" : "kg"}`

                        : `${Math.round(nVal)} ${ing.unit}`;

                      return `

                        <tr onclick="openIngredientEditor(${dayIdx}, '${s.key}', ${iIdx})" class="hover:bg-emerald-50/60 cursor-pointer transition-colors" title="Kliknij, aby zmienić gramaturę lub cenę">

                          <td class="py-2 font-medium text-slate-800">${escapeHtml(trIng(ing.name))} ✏️</td>

                          <td class="py-2 text-right font-semibold text-slate-700">${fmtQty}</td>

                          <td class="py-2 text-right text-slate-400">${ing.waste || 0}%</td>

                          <td class="py-2 text-right text-slate-600">${fmtNet}</td>

                          <td class="py-2 text-right font-bold text-slate-900">${formatMoney(cVal)}</td>

                        </tr>

                      `;

                    }).join("")}

                  </tbody>

                </table>

              </div>

              <button onclick="openIngredientEditor(${dayIdx}, '${s.key}', -1)" class="mt-2.5 text-xs font-semibold text-emerald-700 hover:text-emerald-800 cursor-pointer">+ Dodaj składnik do TTK</button>

            </div>


            <div class="pt-3 border-t border-slate-100 space-y-2.5">

              <div class="bg-slate-50 rounded-xl p-3 text-xs text-slate-700 leading-relaxed">

                <strong class="text-slate-900">${escapeHtml(t("lbl_tech"))}:</strong> ${escapeHtml(dish.tech)}

              </div>

              <div class="flex items-center justify-between text-xs font-bold">

                <span class="text-slate-500">${escapeHtml(t("lbl_unit_cost"))}: <strong class="text-slate-900">${formatMoney(unitCost)}</strong></span>

                <span class="text-emerald-700">${escapeHtml(t("lbl_batch_cost"))} (${s.portions} por.): <strong>${formatMoney(batchCost)}</strong></span>

              </div>

            </div>

          </div>

        `;

      }).join("");

    }


    function setProcurementSplit(split) {

      appState.procurementSplit = split;

      ["all", "wave1", "wave2"].forEach(k => {

        const btn = document.getElementById(`btn-proc-${k}`);

        if (btn) {

          btn.className = k === split

            ? "px-3 py-1.5 rounded-lg text-xs font-semibold bg-white text-slate-900 shadow-sm cursor-pointer"

            : "px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-600 cursor-pointer";

        }

      });

      renderProcurementView();

    }


    function buildProcurementMap() {

      const monThu = parseInt(getElVal("input-portions-mon-thu", "50")) || 50;

      const fri = parseInt(getElVal("input-portions-fri", "70")) || 70;

      const vegShare = (parseInt(getElVal("input-veg-share", "30")) || 30) / 100;

      const dayIndices = appState.procurementSplit === "wave1" ? [0, 1] : (appState.procurementSplit === "wave2" ? [2, 3, 4] : [0, 1, 2, 3, 4]);


      const aggregated = {};

      dayIndices.forEach(dIdx => {

        const day = appState.profile.days[dIdx];

        const total = dIdx === 4 ? fri : monThu;

        const vegP = Math.max(1, Math.round(total * vegShare));

        const meatP = Math.max(1, total - vegP);


        const slotPortions = { meatSoup: meatP, vegSoup: vegP, meatMain: meatP, vegMain: vegP };

        Object.entries(slotPortions).forEach(([slot, count]) => {

          (day[slot].ingredients || []).forEach(ing => {

            const cat = ing.cat || "veg";

            if (!aggregated[cat]) aggregated[cat] = {};

            const key = trIng(ing.name);

            if (!aggregated[cat][key]) {

              aggregated[cat][key] = { name: key, totalUnits: 0, unit: ing.unit === "ml" ? "l" : "kg", totalCost: 0 };

            }

            aggregated[cat][key].totalUnits += (ing.gross * count) / 1000;

            aggregated[cat][key].totalCost += ing.gross * ing.pricePerUnit * count;

          });

        });

      });

      return aggregated;

    }


    function renderProcurementView() {

      const grid = document.getElementById("procurement-categories-grid");

      if (!grid) return;

      const agg = buildProcurementMap();

      const catNames = t("cats");

      const order = ["meat", "veg", "dairy", "dry", "liquids", "spices"];


      grid.innerHTML = order.map(cat => {

        const items = Object.values(agg[cat] || {});

        if (!items.length) return "";

        const catSum = items.reduce((s, it) => s + it.totalCost, 0);

        return `

          <div class="bg-white rounded-2xl border border-slate-200 p-5 shadow-sm flex flex-col justify-between">

            <div>

              <div class="flex items-center justify-between border-b border-slate-100 pb-3 mb-3">

                <h3 class="text-sm font-bold text-slate-900">${escapeHtml(catNames[cat] || cat)}</h3>

                <span class="text-xs font-bold text-emerald-700">${formatMoney(catSum)}</span>

              </div>

              <ul class="space-y-2 text-xs">

                ${items.map(it => `

                  <li class="flex items-center justify-between gap-2 text-slate-700">

                    <span>${escapeHtml(it.name)}</span>

                    <span class="font-bold text-slate-900 shrink-0">${it.totalUnits.toFixed(2)} ${it.unit}</span>

                  </li>

                `).join("")}

              </ul>

            </div>

          </div>

        `;

      }).join("");

    }


    function copyProcurementList() {

      const agg = buildProcurementMap();

      const catNames = t("cats");

      const lines = [`📦 ZAMÓWIENIE SUROWCÓW — ${appState.venueName}`];

      Object.entries(agg).forEach(([cat, map]) => {

        lines.push(`\n[${catNames[cat] || cat}]`);

        Object.values(map).forEach(it => {

          lines.push(`- ${it.name}: ${it.totalUnits.toFixed(2)} ${it.unit}`);

        });

      });

      const ta = document.createElement("textarea");

      ta.value = lines.join("\n");

      document.body.appendChild(ta);

      ta.select();

      document.execCommand("copy");

      document.body.removeChild(ta);

      showToast("📋 Skopiowano listę zakupów do schowka!");

    }


    function setPrintScope(scope) {

      appState.printScope = scope;

      const btnW = document.getElementById("btn-print-week");

      const btnD = document.getElementById("btn-print-day");

      const selWrap = document.getElementById("print-day-selector-wrap");

      if (btnW && btnD) {

        btnW.className = scope === "week" ? "px-3 py-1.5 rounded-lg text-xs font-semibold bg-white text-slate-900 shadow-sm cursor-pointer" : "px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-600 cursor-pointer";

        btnD.className = scope === "day" ? "px-3 py-1.5 rounded-lg text-xs font-semibold bg-white text-slate-900 shadow-sm cursor-pointer" : "px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-600 cursor-pointer";

      }

      if (selWrap) {

        if (scope === "day") selWrap.classList.remove("hidden");

        else selWrap.classList.add("hidden");

      }

      renderPrintAndSmmView();

    }


    function setPrintDayIndex(idx) {

      appState.printDayIndex = idx;

      renderPrintAndSmmView();

    }


    function renderPrintAndSmmView() {

      const listEl = document.getElementById("printable-days-list");

      const smmEl = document.getElementById("smm-post-textarea");

      const venueEl = document.getElementById("print-venue-title");

      const priceEl = document.getElementById("print-combo-price");

      const headEl = document.getElementById("print-main-heading");

      const subEl = document.getElementById("print-subtitle-range");

      if (!listEl) return;


      const dict = I18N[appState.lang] || I18N.pl;

      const mondayIso = getElVal("input-week-start", appState.weekStartDate);

      const guestPrice = parseFloat(getElVal("input-guest-price", "39")) || 39;


      if (venueEl) venueEl.textContent = (appState.venueName || "LUNCH PILOT BISTRO").toUpperCase();

      if (priceEl) priceEl.textContent = formatMoney(guestPrice);


      const indices = appState.printScope === "day" ? [appState.printDayIndex] : [0, 1, 2, 3, 4];

      if (headEl) headEl.textContent = appState.printScope === "day" ? t("print_day_header") : t("print_menu_header");

      if (subEl) {

        subEl.textContent = appState.printScope === "day"

          ? `${dict.days[appState.printDayIndex]} (${addDaysToIso(mondayIso, appState.printDayIndex)})`

          : `${addDaysToIso(mondayIso, 0)} — ${addDaysToIso(mondayIso, 4)}`;

      }


      listEl.innerHTML = indices.map(idx => {

        const day = appState.profile.days[idx];

        const dateStr = addDaysToIso(mondayIso, idx);

        const allergens = Array.from(new Set([

          ...detectAllergensForDish(day.meatSoup),

          ...detectAllergensForDish(day.vegSoup),

          ...detectAllergensForDish(day.meatMain),

          ...detectAllergensForDish(day.vegMain)

        ])).join(", ");


        return `

          <div class="border-b border-slate-200 pb-4 last:border-none">

            <div class="flex items-baseline justify-between mb-1.5">

              <span class="font-serif text-base sm:text-lg font-bold text-slate-900">${escapeHtml(dict.days[idx])}</span>

              <span class="text-xs text-slate-500">${escapeHtml(dateStr)}</span>

            </div>

            <div class="space-y-1 text-xs sm:text-sm text-slate-800">

              <div><strong>🍲 Zupa:</strong> ${escapeHtml(day.meatSoup.title)} <span class="text-slate-400">/</span> 🌱 ${escapeHtml(day.vegSoup.title)}</div>

              <div><strong>🍖 Danie dnia:</strong> ${escapeHtml(day.meatMain.base)} • <em>${escapeHtml(day.meatMain.side)}</em> • ${escapeHtml(day.meatMain.salad)}</div>

              <div><strong>🌱 Wersja Wege:</strong> ${escapeHtml(day.vegMain.base)} • <em>${escapeHtml(day.vegMain.side)}</em> • ${escapeHtml(day.vegMain.salad)}</div>

              ${allergens ? `<div class="text-[11px] text-slate-400 pt-0.5">Alergeny: ${escapeHtml(allergens)}</div>` : ""}

            </div>

          </div>

        `;

      }).join("");


      if (smmEl) {

        const smmLines = [

          `🍽️ LUNCH MENU — ${appState.venueName} (${addDaysToIso(mondayIso, 0)} – ${addDaysToIso(mondayIso, 4)})`,

          `⏰ Zapraszamy codziennie 12:00 – 16:00 | Zestaw dnia: ${formatMoney(guestPrice)}\n`

        ];

        appState.profile.days.forEach((day, idx) => {

          smmLines.push(`📅 ${dict.days[idx].toUpperCase()} (${addDaysToIso(mondayIso, idx)})`);

          smmLines.push(`🍲 ${day.meatSoup.title}`);

          smmLines.push(`🌱 ${day.vegSoup.title}`);

          smmLines.push(`🍖 ${day.meatMain.base} (${day.meatMain.side})`);

          smmLines.push(`🥗 ${day.vegMain.base} (${day.vegMain.side})\n`);

        });

        smmLines.push(`📞 Rezerwacje stolików i zamówienia na wynos w wiadomości prywatnej!`);

        smmEl.value = smmLines.join("\n");

      }

    }


    function copySmmPost() {

      const ta = document.getElementById("smm-post-textarea");

      if (!ta) return;

      ta.select();

      document.execCommand("copy");

      showToast("📋 Skopiowano gotowy post na Instagram / Telegram!");

    }


    // =========================================================================

    // 10. EVENT & CATERING ORGANIZER MODULE

    // =========================================================================

    function renderEventView() {

      const guests = Math.max(1, parseInt(getElVal("ev-guests", "45")) || 45);

      const pricePerGuest = Math.max(0, parseFloat(getElVal("ev-price-per-guest", "145")) || 145);

      const evName = getElVal("ev-name", "Bankiet Firmowy");

      const evDate = getElVal("ev-date", appState.weekStartDate);


      const foodCostPerGuest = appState.eventCourses.reduce((s, c) => s + (Number(c.costPerGuest) || 0), 0);

      const totalRevenue = guests * pricePerGuest;

      const totalFoodCost = guests * foodCostPerGuest;

      const netMargin = totalRevenue - totalFoodCost;

      const fcPct = pricePerGuest > 0 ? ((foodCostPerGuest / pricePerGuest) * 100).toFixed(1) : "0.0";


      const kpiStrip = document.getElementById("event-kpi-strip");

      if (kpiStrip) {

        kpiStrip.innerHTML = `

          <div class="bg-white rounded-2xl p-4 border border-slate-200 shadow-sm">

            <div class="text-xs text-slate-500">Wartość oferty (Brutto)</div>

            <div class="text-xl font-extrabold text-slate-900 mt-1">${formatMoney(totalRevenue)}</div>

          </div>

          <div class="bg-white rounded-2xl p-4 border border-slate-200 shadow-sm">

            <div class="text-xs text-slate-500">Koszt surowca (Food Cost)</div>

            <div class="text-xl font-extrabold text-amber-700 mt-1">${formatMoney(totalFoodCost)} <span class="text-xs font-semibold">(${fcPct}%)</span></div>

          </div>

          <div class="bg-white rounded-2xl p-4 border border-slate-200 shadow-sm">

            <div class="text-xs text-slate-500">Zysk operacyjny z imprezy</div>

            <div class="text-xl font-extrabold text-emerald-700 mt-1">+${formatMoney(netMargin)}</div>

          </div>

          <div class="bg-white rounded-2xl p-4 border border-slate-200 shadow-sm">

            <div class="text-xs text-slate-500">Koszt surowca / 1 gość</div>

            <div class="text-xl font-extrabold text-slate-900 mt-1">${formatMoney(foodCostPerGuest)}</div>

          </div>

        `;

      }


      const listEl = document.getElementById("event-courses-list");

      if (listEl) {

        listEl.innerHTML = appState.eventCourses.map((c, idx) => `

          <div class="rounded-xl border border-slate-200 bg-slate-50/70 p-3 space-y-2">

            <div class="flex items-center justify-between gap-2">

              <input type="text" value="${escapeHtml(c.type)}" onchange="appState.eventCourses[${idx}].type=this.value; renderEventView()" class="text-xs font-bold text-purple-800 bg-purple-100/70 px-2 py-0.5 rounded-md w-32 outline-none" />

              <div class="flex items-center gap-1.5">

                <span class="text-[11px] text-slate-500">Koszt/os:</span>

                <input type="number" step="0.5" value="${c.costPerGuest}" onchange="appState.eventCourses[${idx}].costPerGuest=parseFloat(this.value)||0; renderEventView()" class="w-16 text-xs font-bold text-slate-900 bg-white border border-slate-300 rounded-md px-1.5 py-0.5" />

                <button onclick="appState.eventCourses.splice(${idx},1); renderEventView()" class="text-xs text-rose-600 font-bold px-1.5 cursor-pointer">✕</button>

              </div>

            </div>

            <input type="text" value="${escapeHtml(c.title)}" onchange="appState.eventCourses[${idx}].title=this.value; renderEventView()" class="w-full text-xs sm:text-sm font-semibold text-slate-900 bg-white border border-slate-200 rounded-lg px-2.5 py-1.5 outline-none focus:border-purple-500" />

          </div>

        `).join("");

      }


      const printVenue = document.getElementById("ev-print-venue");

      const printTitle = document.getElementById("ev-print-title");

      const printMeta = document.getElementById("ev-print-meta");

      const printCourses = document.getElementById("ev-print-courses");

      const printTotal = document.getElementById("ev-print-total");


      if (printVenue) printVenue.textContent = (appState.venueName || "RESTAURACJA").toUpperCase();

      if (printTitle) printTitle.textContent = evName.toUpperCase();

      if (printMeta) printMeta.textContent = `Data: ${evDate} • ${guests} gości • ${formatMoney(pricePerGuest)} / os.`;

      if (printTotal) printTotal.textContent = `Razem: ${formatMoney(totalRevenue)}`;

      if (printCourses) {

        printCourses.innerHTML = appState.eventCourses.map(c => `

          <div class="border-b border-slate-100 pb-2">

            <div class="text-[11px] font-bold uppercase tracking-wider text-purple-800">${escapeHtml(c.type)}</div>

            <div class="font-serif text-sm sm:text-base font-semibold text-slate-900">${escapeHtml(c.title)}</div>

          </div>

        `).join("");

      }

    }


    function addEventCourse() {

      appState.eventCourses.push({

        type: "Dodatek / Serwis",

        title: "Nowa pozycja bankietowa",

        costPerGuest: 6.50,

        allergens: "Gluten"

      });

      renderEventView();

    }


    function importFromLunchToEvent() {

      const d0 = appState.profile.days[0];

      const d2 = appState.profile.days[2];

      appState.eventCourses = [

        { type: "Zupa powitalna", title: d0.vegSoup.title, costPerGuest: +(computeDishCost(d0.vegSoup) * 1.15).toFixed(2), allergens: "Laktoza" },

        { type: "Danie główne I", title: `${d0.meatMain.base} — ${d0.meatMain.side}`, costPerGuest: +(computeDishCost(d0.meatMain) * 1.15).toFixed(2), allergens: "Laktoza" },

        { type: "Danie główne II", title: `${d2.meatMain.base} — ${d2.meatMain.side}`, costPerGuest: +(computeDishCost(d2.meatMain) * 1.15).toFixed(2), allergens: "Gorczyca" },

        { type: "Opcja Wegetariańska", title: d2.vegMain.base, costPerGuest: +(computeDishCost(d2.vegMain) * 1.15).toFixed(2), allergens: "Gluten, Laktoza" }

      ];

      renderEventView();

      showToast("📥 Zaimportowano hity z aktualnego menu lunchowego!");

    }


    async function generateEventProposalAi() {

      const guests = getElVal("ev-guests", "45");

      const evType = getElVal("ev-type", "corporate");

      showToast("✨ AI układa ofertę bankietową...");

      const prompt = `Ułóż 5-daniowe menu bankietowe dla wydarzenia typu "${evType}" dla ${guests} gości w języku "${appState.lang}". Zwróć JSON z tablicą courses (type, title, costPerGuest w PLN).`;

      const schema = {

        type: "OBJECT",

        properties: {

          courses: {

            type: "ARRAY",

            items: {

              type: "OBJECT",

              properties: {

                type: { type: "STRING" },

                title: { type: "STRING" },

                costPerGuest: { type: "NUMBER" }

              },

              required: ["type", "title", "costPerGuest"]

            }

          }

        },

        required: ["courses"]

      };

      try {

        const res = await callGeminiJson(prompt, schema);

        if (res && Array.isArray(res.courses) && res.courses.length) {

          const metaScale = (appState.lang === "pl") ? 1 : ((COUNTRY_META[appState.lang]?.scale) || 0.25);

          appState.eventCourses = res.courses.map(c => ({

            type: c.type,

            title: c.title,

            costPerGuest: +((Number(c.costPerGuest) || 8) * metaScale).toFixed(2),

            allergens: "Gluten, Laktoza"

          }));

          renderEventView();

          showToast("✨ Gotowe! Oferta bankietowa została wygenerowana.");

        }

      } catch (e) {

        importFromLunchToEvent();

      }

    }


    // =========================================================================

    // 11. HISTORY, NAVIGATION, SETTINGS & LOCALSTORAGE PERSISTENCE

    // =========================================================================

    function saveWeekToHistory() {

      const mondayIso = getElVal("input-week-start", appState.weekStartDate);

      const dishes = [];

      appState.profile.days.forEach(d => {

        dishes.push(d.meatSoup.title, d.vegSoup.title, d.meatMain.base, d.vegMain.base);

      });

      appState.historyLog.unshift({

        id: Date.now(),

        weekStart: mondayIso,

        weekEnd: addDaysToIso(mondayIso, 4),

        savedAt: Date.now(),

        dishes

      });

      savePersistentState();

      recalculateAll();

      showToast("💾 " + t("hist_saved_toast"));

    }


    function renderHistoryView() {

      const container = document.getElementById("history-log-container");

      if (!container) return;

      if (!appState.historyLog.length) {

        container.innerHTML = `<div class="bg-white rounded-2xl border border-slate-200 p-8 text-center text-sm text-slate-500">${escapeHtml(t("hist_empty"))}</div>`;

        return;

      }

      container.innerHTML = appState.historyLog.map(entry => `

        <div class="bg-white rounded-2xl border border-slate-200 p-4 sm:p-5 shadow-sm flex flex-col sm:flex-row sm:items-center justify-between gap-3">

          <div>

            <div class="text-sm font-bold text-slate-900">📅 Tydzień: ${escapeHtml(entry.weekStart)} — ${escapeHtml(entry.weekEnd)}</div>

            <div class="text-xs text-slate-500 mt-1">${escapeHtml((entry.dishes || []).slice(0, 8).join(" • "))}...</div>

          </div>

          <span class="text-xs font-bold px-3 py-1 rounded-full bg-indigo-50 text-indigo-700 border border-indigo-200 shrink-0">🛡️ Aktywna blokada 30 dni</span>

        </div>

      `).join("");

    }


    function clearHistoryLog() {

      appState.historyLog = [];

      savePersistentState();

      recalculateAll();

      showToast("🗑️ Wyczyszczono historię menu.");

    }


    function switchTab(tabName) {

      appState.activeTab = tabName;

      document.querySelectorAll(".workspace-view").forEach(v => v.classList.add("hidden"));

      const target = document.getElementById(`view-${tabName}`);

      if (target) target.classList.remove("hidden");


      document.querySelectorAll(".nav-tab").forEach(btn => {

        const isAct = btn.getAttribute("data-tab") === tabName;

        if (btn.classList.contains("shrink-0")) {

          btn.className = isAct

            ? "nav-tab shrink-0 px-3 py-1.5 rounded-lg text-xs font-semibold bg-emerald-600 text-white"

            : "nav-tab shrink-0 px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 bg-slate-800";

        } else {

          btn.className = isAct

            ? "nav-tab px-3 py-1.5 rounded-lg text-xs font-semibold transition-all bg-emerald-600 text-white shadow-sm cursor-pointer"

            : "nav-tab px-3 py-1.5 rounded-lg text-xs font-semibold text-slate-300 hover:text-white hover:bg-slate-700/50 transition-all cursor-pointer";

        }

      });

    }


    function changeLanguage(langCode) {

      appState.lang = langCode;

      const dict = I18N[langCode] || I18N.pl;

      document.documentElement.lang = langCode;

      document.documentElement.dir = dict.dir || "ltr";


      appState.currency = dict.currency;

      appState.profile = buildLocalizedProfile(langCode);


      setElVal("input-target-fc", dict.defaultFc);

      setElVal("input-guest-price", dict.defaultGuestPrice);


      const countryBadge = document.getElementById("ui-country-badge");

      if (countryBadge) countryBadge.textContent = dict.countryBadge;


      const cuisinePill = document.getElementById("cuisine-profile-pill");

      if (cuisinePill) cuisinePill.textContent = dict.cuisinePill;


      document.querySelectorAll(".currency-label").forEach(el => { el.textContent = dict.currency; });

      document.querySelectorAll("[data-i18n]").forEach(el => {

        const key = el.getAttribute("data-i18n");

        if (dict[key]) el.textContent = dict[key];

      });


      const daySel = document.getElementById("print-day-select");

      if (daySel) {

        daySel.innerHTML = dict.days.map((dName, i) => `<option value="${i}">${dName}</option>`).join("");

      }


      enforceWeeklyUniqueness(-1);

      recalculateAll();

    }


    function updateVenueNameLive(val) {

      appState.venueName = val;

      setElVal("setting-venue-name", val);

      renderPrintAndSmmView();

      renderEventView();

      savePersistentState();

    }


    function updateVegShareLabel() {

      const val = getElVal("input-veg-share", "30");

      const lbl = document.getElementById("veg-share-label");

      if (lbl) lbl.textContent = `${val}%`;

    }


    function onStockInputChange() {

      recalculateAll();

    }


    function applyStockNow() {

      generateFullWeek(true);

    }


    function onRegularMenuChange() {

      savePersistentState();

    }


    function onSeasonChange() {

      generateFullWeek(false);

    }


    function openSettingsModal() {

      setElVal("setting-venue-name", appState.venueName);

      setElVal("setting-currency", appState.currency);

      const modal = document.getElementById("settings-modal");

      if (modal) modal.classList.remove("hidden");

    }


    function closeSettingsModal() {

      const modal = document.getElementById("settings-modal");

      if (modal) modal.classList.add("hidden");

    }


    function saveSettings() {

      appState.venueName = getElVal("setting-venue-name", appState.venueName).trim() || "Restauracja";

      appState.currency = getElVal("setting-currency", appState.currency);

      setElVal("header-venue-input", appState.venueName);

      document.querySelectorAll(".currency-label").forEach(el => { el.textContent = appState.currency; });

      closeSettingsModal();

      recalculateAll();

      renderEventView();

      showToast("✅ Zapisano ustawienia lokalu");

    }


    function savePersistentState() {

      try {

        const payload = {

          lang: appState.lang,

          currency: appState.currency,

          venueName: appState.venueName,

          weekStartDate: getElVal("input-week-start", appState.weekStartDate),

          targetFc: getElVal("input-target-fc", "13.50"),

          guestPrice: getElVal("input-guest-price", "39"),

          monThu: getElVal("input-portions-mon-thu", "50"),

          fri: getElVal("input-portions-fri", "70"),

          vegShare: getElVal("input-veg-share", "30"),

          stock: getElVal("input-stock", ""),

          regularMenu: getElVal("input-regular-menu", ""),

          season: getElVal("input-season", "autumn"),

          days: appState.profile.days,

          historyLog: appState.historyLog,

          aiCustomPool: appState.aiCustomPool,

          eventCourses: appState.eventCourses

        };

        localStorage.setItem(STORAGE_KEY, JSON.stringify(payload));

      } catch (e) {}

    }


    function loadPersistentState() {

      try {

        const raw = localStorage.getItem(STORAGE_KEY);

        if (!raw) return false;

        const data = JSON.parse(raw);

        if (!data || !Array.isArray(data.days) || data.days.length !== 5) return false;


        appState.lang = data.lang || "pl";

        appState.currency = data.currency || "zł";

        appState.venueName = data.venueName || "Restauracja Studio Buffo";

        appState.weekStartDate = data.weekStartDate || getNextOrCurrentMonday();

        appState.profile.days = data.days;

        appState.historyLog = Array.isArray(data.historyLog) ? data.historyLog : [];

        if (data.aiCustomPool) appState.aiCustomPool = data.aiCustomPool;

        if (Array.isArray(data.eventCourses)) appState.eventCourses = data.eventCourses;


        const dict = I18N[appState.lang] || I18N.pl;

        document.documentElement.lang = appState.lang;

        document.documentElement.dir = dict.dir || "ltr";


        setElVal("lang-selector", appState.lang);

        setElVal("header-venue-input", appState.venueName);

        setElVal("input-week-start", appState.weekStartDate);

        setElVal("ev-date", appState.weekStartDate);

        setElVal("input-target-fc", data.targetFc || dict.defaultFc);

        setElVal("input-guest-price", data.guestPrice || dict.defaultGuestPrice);

        setElVal("input-portions-mon-thu", data.monThu || 50);

        setElVal("input-portions-fri", data.fri || 70);

        setElVal("input-veg-share", data.vegShare || 30);

        setElVal("input-stock", data.stock || "");

        setElVal("input-regular-menu", data.regularMenu || "");

        setElVal("input-season", data.season || "autumn");


        updateVegShareLabel();

        document.querySelectorAll(".currency-label").forEach(el => { el.textContent = appState.currency; });

        document.querySelectorAll("[data-i18n]").forEach(el => {

          const key = el.getAttribute("data-i18n");

          if (dict[key]) el.textContent = dict[key];

        });


        const daySel = document.getElementById("print-day-select");

        if (daySel) {

          daySel.innerHTML = dict.days.map((dName, i) => `<option value="${i}">${dName}</option>`).join("");

        }

        return true;

      } catch (e) {

        return false;

      }

    }


    async function swapDish(dayIdx, slotKey, silent = false) {

      const currentDish = appState.profile.days[dayIdx][slotKey];

      const currentId = getDishIdentifier(currentDish);

      const blockedHistory = getRecentBlockedDishNames();

      const regularTerms = getRegularMenuBlockedTerms();

      const stockTokens = getZeroWasteStockKeywords();

      const selectedSeason = getElVal("input-season", "autumn");

      const isSoup = slotKey === "meatSoup" || slotKey === "vegSoup";


      const weekUsed = new Set();

      appState.profile.days.forEach(d => {

        if (isSoup) {

          weekUsed.add(getDishIdentifier(d.meatSoup));

          weekUsed.add(getDishIdentifier(d.vegSoup));

        } else {

          weekUsed.add(getDishIdentifier(d[slotKey]));

        }

      });


      const pool = buildSlotPool(slotKey);

      let candidates = pool.filter(c => {

        const id = getDishIdentifier(c);

        return id !== currentId && !appState.sessionSeenDishes.has(id) && !weekUsed.has(id) && !blockedHistory.has(id) && !isDishBlockedByRegularMenu(c, regularTerms);

      });


      if (candidates.length < 4) {

        autoExpandDishDatabase(false);

      }


      if (!candidates.length) {

        pool.forEach(c => appState.sessionSeenDishes.delete(getDishIdentifier(c)));

        candidates = pool.filter(c => {

          const id = getDishIdentifier(c);

          return id !== currentId && !weekUsed.has(id) && !blockedHistory.has(id) && !isDishBlockedByRegularMenu(c, regularTerms);

        });

      }


      if (!candidates.length) {

        candidates = pool.filter(c => getDishIdentifier(c) !== currentId && !weekUsed.has(getDishIdentifier(c)));

      }

      if (!candidates.length) return;


      candidates.sort((a, b) => (scoreCandidate(b, stockTokens, selectedSeason) - scoreCandidate(a, stockTokens, selectedSeason)) + (Math.random() - 0.5) * 2.5);

      const chosen = JSON.parse(JSON.stringify(candidates[0]));


      if (!isSoup) {

        verifyAndHarmonizeDish(chosen, true);

      }


      appState.sessionSeenDishes.add(getDishIdentifier(chosen));

      appState.profile.days[dayIdx][slotKey] = chosen;

      enforceWeeklyUniqueness(dayIdx);

      recalculateAll();

      if (!silent) {

        const matchedZw = getMatchedStockBadges(chosen, stockTokens);

        showToast(matchedZw ? `♻️ ${t("btn_swap")} (${matchedZw})` : `🔄 ${t("btn_swap")}`);

      }

    }


    async function generateFullWeek(prioritizeStock = false) {

      const banner = document.getElementById("ai-loading-banner");

      if (banner) banner.classList.remove("hidden");


      appState.profile.days.forEach(d => {

        ["meatSoup", "vegSoup", "meatMain", "vegMain"].forEach(s => {

          if (d[s]) appState.sessionSeenDishes.add(getDishIdentifier(d[s]));

        });

      });


      const blockedHistory = getRecentBlockedDishNames();

      const regularTerms = getRegularMenuBlockedTerms();

      const stockTokens = getZeroWasteStockKeywords();

      const selectedSeason = getElVal("input-season", "autumn");

      const slots = ["meatSoup", "vegSoup", "meatMain", "vegMain"];

      const usedSoupsThisWeek = new Set();


      for (const slotKey of slots) {

        const pool = buildSlotPool(slotKey);

        const usedThisWeek = new Set();

        const isSoup = slotKey === "meatSoup" || slotKey === "vegSoup";


        for (let dayIdx = 0; dayIdx < 5; dayIdx++) {

          let candidates = pool.filter(c => {

            const id = getDishIdentifier(c);

            const isUsedInWeek = isSoup ? (usedThisWeek.has(id) || usedSoupsThisWeek.has(id)) : usedThisWeek.has(id);

            return !appState.sessionSeenDishes.has(id) && !isUsedInWeek && !blockedHistory.has(id) && !isDishBlockedByRegularMenu(c, regularTerms);

          });


          if (!candidates.length) {

            pool.forEach(c => appState.sessionSeenDishes.delete(getDishIdentifier(c)));

            candidates = pool.filter(c => {

              const id = getDishIdentifier(c);

              const isUsedInWeek = isSoup ? (usedThisWeek.has(id) || usedSoupsThisWeek.has(id)) : usedThisWeek.has(id);

              return !isUsedInWeek && !blockedHistory.has(id) && !isDishBlockedByRegularMenu(c, regularTerms);

            });

          }


          if (!candidates.length) {

            candidates = pool.filter(c => {

              const id = getDishIdentifier(c);

              return isSoup ? (!usedThisWeek.has(id) && !usedSoupsThisWeek.has(id)) : !usedThisWeek.has(id);

            });

          }

          if (!candidates.length) candidates = pool;


          candidates.sort((a, b) => {

            const scoreDiff = scoreCandidate(b, stockTokens, selectedSeason) - scoreCandidate(a, stockTokens, selectedSeason);

            const jitter = (Math.random() - 0.5) * (prioritizeStock ? 0.5 : 2.5);

            return scoreDiff + jitter;

          });


          const picked = JSON.parse(JSON.stringify(candidates[0]));

          if (!isSoup) {

            verifyAndHarmonizeDish(picked, true);

          }

          const pickedId = getDishIdentifier(picked);

          usedThisWeek.add(pickedId);

          if (isSoup) usedSoupsThisWeek.add(pickedId);

          appState.sessionSeenDishes.add(pickedId);

          appState.profile.days[dayIdx][slotKey] = picked;

        }

      }


      enforceWeeklyUniqueness(-1);

      autoExpandDishDatabase(false);


      if (banner) banner.classList.add("hidden");

      runFullMenuAudit(false);

      recalculateAll();

      if (prioritizeStock && stockTokens.length) {

        showToast("♻️ Menu ułożone pod stany magazynowe (Zero-Waste)!");

      }

    }


    window.onload = function () {

      const restored = loadPersistentState();

      if (!restored) {

        const dict = I18N[appState.lang];

        setElVal("header-venue-input", appState.venueName);

        setElVal("input-week-start", appState.weekStartDate);

        setElVal("ev-date", appState.weekStartDate);

        const daySel = document.getElementById("print-day-select");

        if (daySel) {

          daySel.innerHTML = dict.days.map((dName, i) => `<option value="${i}">${dName}</option>`).join("");

        }

      }

      enforceWeeklyUniqueness(-1);

      runFullMenuAudit(false);

      recalculateAll();

      renderEventView();

    };

  </script>

</body>

</html>