feat(recipes): add image upload, fix save 500, seed HelloFresh data

- Store hero image as base64 data URI in text column (V023 migration)
- Add file upload UI to RecipeForm with FileReader preview
- Remove isChildFriendly from RecipeCreateRequest (no form field)
- Fix 500 on save: effort values now lowercase, serves/cookTimeMin changed
  from primitive short to nullable Integer to survive omitted fields
- Fix empty categories panel: removed stale tagType=category filter
- Group category tags by type with German headings in recipe form
- Split SuggestionResponse.SuggestionRecipe (no image) from SlotRecipe
- Seed 11 HelloFresh recipes with ingredients, steps and tags (V101)
- Add frontend e2e scaffold, specs and dev yml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 20:23:28 +02:00
parent 116e400a91
commit 520dae5adf
34 changed files with 9862 additions and 84 deletions

View File

@@ -0,0 +1,755 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Planner C+E — Drei Zustände</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:wght@300;400&family=DM+Sans:wght@400;500;600&family=DM+Mono&display=swap" rel="stylesheet">
<style>
:root {
--page: #fafaf7;
--surface: #f5f4ee;
--subtle: #edecea;
--border: #d8d7d0;
--text: #1c1c18;
--muted: #6b6a63;
--gt: #e8f5ea; --gl: #aedcb0; --g: #3d8c4a; --gd: #2e6e39;
--yt: #fdf6d8; --yl: #f9e08a; --y: #f2c12e; --yx: #8a6800;
--pt: #eeedfe; --p: #534ab7;
--ot: #fef0e6; --o: #e8862a; --od: #b46820;
--err: #dc4c3e;
--r-sm: 4px; --r-md: 6px; --r-lg: 10px; --r-xl: 16px; --r-full: 9999px;
--sh-card: 0 1px 3px rgba(28,28,24,.06),0 1px 2px rgba(28,28,24,.04);
--sh-raised: 0 4px 12px rgba(28,28,24,.08),0 2px 4px rgba(28,28,24,.04);
--fd: 'Fraunces', Georgia, serif;
--fs: 'DM Sans', system-ui, sans-serif;
--fm: 'DM Mono', monospace;
}
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:var(--fs);background:#dddcd7;color:var(--text);padding:40px 24px 80px;line-height:1.4;}
/* ── Page chrome ─── */
.eyebrow{font-family:var(--fs);font-size:11px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin-bottom:6px;}
.title{font-family:var(--fd);font-size:34px;font-weight:300;margin-bottom:6px;}
.sub{font-family:var(--fs);font-size:14px;color:var(--muted);max-width:700px;line-height:1.65;margin-bottom:20px;}
.flow-legend{display:flex;gap:20px;margin-bottom:44px;flex-wrap:wrap;}
.fl-item{display:flex;align-items:center;gap:8px;font-family:var(--fs);font-size:12px;color:var(--muted);}
.fl-dot{width:10px;height:10px;border-radius:50%;}
/* ── Frame chrome ─── */
.state-block{margin-bottom:60px;}
.state-label{display:flex;align-items:center;gap:10px;margin-bottom:12px;}
.state-num{font-family:var(--fm);font-size:11px;background:var(--subtle);color:var(--muted);padding:3px 8px;border-radius:var(--r-sm);}
.state-name{font-family:var(--fd);font-size:22px;font-weight:300;}
.state-when{font-family:var(--fs);font-size:12px;color:var(--muted);margin-left:auto;}
.note{font-family:var(--fs);font-size:12px;color:var(--muted);border-left:3px solid var(--border);padding:10px 14px;margin-top:14px;line-height:1.6;}
.note strong{color:var(--text);font-weight:500;}
/* ── Desktop frame ─── */
.frame{display:flex;flex-direction:column;background:var(--page);border:1px solid var(--border);border-radius:var(--r-lg);overflow:hidden;box-shadow:var(--sh-raised);}
/* Topbar */
.tb{display:flex;align-items:center;gap:7px;padding:11px 18px;border-bottom:1px solid var(--border);background:var(--page);flex-shrink:0;}
.tb-h1{font-family:var(--fd);font-size:17px;font-weight:300;}
.tb-range{font-family:var(--fs);font-size:11px;color:var(--muted);}
.tb-arr{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border:1px solid var(--border);border-radius:var(--r-md);font-size:13px;color:var(--muted);}
.tb-btn{height:28px;padding:0 10px;border:1px solid var(--border);border-radius:var(--r-md);font-family:var(--fs);font-size:11px;font-weight:500;letter-spacing:.04em;color:var(--text);background:var(--page);}
.tb-ml{margin-left:auto;}
.tb-pri{background:var(--gd);color:#fff;border:none;}
/* 3-panel body */
.body{display:flex;flex:1;overflow:hidden;}
/* Sidebar */
.sb{width:184px;flex-shrink:0;border-right:1px solid var(--border);background:var(--surface);padding:13px;display:flex;flex-direction:column;gap:13px;overflow-y:auto;}
.sb-lbl{font-family:var(--fs);font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);margin-bottom:5px;}
.score-box{background:var(--yt);border:1px solid var(--yl);border-radius:var(--r-md);padding:10px;}
.sc-big{font-family:var(--fd);font-size:27px;font-weight:300;line-height:1;}
.sc-den{font-family:var(--fs);font-size:11px;color:var(--muted);}
.pbar{height:4px;border-radius:var(--r-full);background:var(--yl);overflow:hidden;margin-top:6px;}
.pb-fill{height:100%;border-radius:var(--r-full);background:var(--y);}
.pbg{background:var(--g);}
.pbt{background:var(--border);}
.sr{display:flex;align-items:center;gap:6px;margin-top:6px;}
.sr-l{font-family:var(--fs);font-size:10px;color:var(--muted);width:68px;flex-shrink:0;}
.sr-b{flex:1;height:3px;border-radius:var(--r-full);background:var(--border);overflow:hidden;}
.sr-f{height:100%;border-radius:var(--r-full);}
.sr-v{font-family:var(--fm);font-size:9px;color:var(--muted);width:18px;text-align:right;}
.w-item{font-family:var(--fs);font-size:10px;color:var(--yx);margin-top:4px;line-height:1.4;}
.dp{display:flex;gap:2px;margin-top:5px;}
.dp-s{flex:1;height:4px;border-radius:var(--r-full);}
/* Right panel */
.rp{width:228px;flex-shrink:0;border-left:1px solid var(--border);background:var(--page);padding:13px;display:flex;flex-direction:column;overflow-y:auto;}
.rp-lbl{font-family:var(--fs);font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);margin-bottom:7px;}
.rp-name{font-family:var(--fd);font-size:16px;font-weight:300;line-height:1.35;}
.rp-meta{font-family:var(--fs);font-size:11px;color:var(--muted);margin-top:3px;}
.rp-btn{display:block;width:100%;padding:7px;border-radius:var(--r-md);border:1px solid var(--border);background:var(--page);font-family:var(--fs);font-size:11px;font-weight:500;letter-spacing:.04em;text-align:center;color:var(--text);margin-top:5px;}
.rp-pri{background:var(--gd);color:#fff;border:none;}
.rp-err{color:var(--err);border-color:var(--err);background:transparent;}
.hr{height:1px;background:var(--border);margin:10px 0;}
/* Main area */
.main{flex:1;overflow-y:auto;padding:13px;}
/* Calendar grid */
.grid7{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;}
.d-abbr{font-family:var(--fs);font-size:9px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);text-align:center;margin-bottom:3px;}
.d-badge{width:20px;height:20px;border-radius:var(--r-full);display:flex;align-items:center;justify-content:center;font-family:var(--fs);font-size:10px;font-weight:500;margin:0 auto 5px;color:var(--text);}
.db-t{background:var(--y);color:#fff;}
.db-s{background:var(--gt);color:var(--gd);}
.tile{border-radius:var(--r-md);border:1px solid var(--border);background:var(--surface);padding:7px;cursor:pointer;box-shadow:var(--sh-card);}
.tile-t{border:2px solid var(--y);background:var(--yt);}
.tile-s{border:2px solid var(--g);background:var(--gt);}
.tile-e{border-style:dashed;background:transparent;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--muted);gap:2px;}
.tile-e-s{border:2px dashed var(--g);background:var(--gt);}
.tr{font-family:var(--fd);font-size:11px;font-weight:300;color:var(--text);line-height:1.3;}
.tm{font-family:var(--fs);font-size:9px;color:var(--muted);margin-top:2px;}
.tbdg{display:inline-flex;align-items:center;padding:1px 4px;border-radius:2px;font-family:var(--fs);font-size:8px;font-weight:500;margin-top:3px;}
.b-e{background:var(--gt);color:var(--gd);}
.b-m{background:var(--yt);color:var(--yx);}
.b-h{background:var(--ot);color:var(--od);}
/* Section header */
.sec-hd{display:flex;align-items:center;justify-content:space-between;margin-bottom:9px;padding-bottom:7px;border-bottom:1px solid var(--border);}
.sec-t{font-family:var(--fs);font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);}
.sec-link{font-family:var(--fs);font-size:10px;font-weight:500;color:var(--yx);}
/* Agenda list */
.agenda{display:flex;flex-direction:column;gap:4px;}
.ag-item{display:flex;align-items:center;gap:10px;padding:7px 10px;border-radius:var(--r-md);border:1px solid var(--border);background:var(--surface);cursor:pointer;}
.ag-item:hover{border-color:var(--gl);}
.ag-t{border-style:dashed;background:transparent;}
.ag-t .ag-day{color:var(--muted);}
.ag-day{font-family:var(--fs);font-size:10px;color:var(--muted);width:48px;flex-shrink:0;}
.ag-name{font-family:var(--fd);font-size:13px;font-weight:300;flex:1;}
.ag-meta{font-family:var(--fs);font-size:10px;color:var(--muted);flex-shrink:0;}
.ag-action{font-family:var(--fs);font-size:10px;font-weight:500;color:var(--gd);flex-shrink:0;}
.ag-today-dot{width:6px;height:6px;border-radius:50%;background:var(--y);flex-shrink:0;}
.ag-item-today{border-color:var(--yl);background:var(--yt);}
/* Suggestions for empty days (in agenda / standalone) */
.sug-block{margin-top:14px;}
.sug-day-hd{display:flex;align-items:center;gap:6px;margin-bottom:6px;}
.sug-day-name{font-family:var(--fs);font-size:11px;font-weight:500;color:var(--text);}
.sug-empty-pill{font-family:var(--fs);font-size:9px;background:var(--subtle);color:var(--muted);padding:1px 6px;border-radius:var(--r-full);}
.sug-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;}
.sc-item{border:1px solid var(--border);border-radius:var(--r-md);background:var(--surface);padding:9px;cursor:pointer;transition:border-color .1s,box-shadow .1s;}
.sc-item:hover{border-color:var(--gl);box-shadow:var(--sh-raised);}
.sc-name{font-family:var(--fd);font-size:12px;font-weight:300;line-height:1.35;color:var(--text);}
.sc-meta{font-family:var(--fs);font-size:10px;color:var(--muted);margin-top:3px;}
.sc-tag{font-family:var(--fs);font-size:9px;font-weight:500;padding:2px 6px;border-radius:2px;margin-top:6px;display:inline-block;background:var(--gt);color:var(--gd);}
.sc-tag-y{background:var(--yt);color:var(--yx);}
.sc-add{display:flex;align-items:center;justify-content:center;height:100%;min-height:72px;border:1px dashed var(--border);border-radius:var(--r-md);font-family:var(--fs);font-size:11px;color:var(--muted);cursor:pointer;}
.sc-add:hover{border-color:var(--gl);color:var(--gd);}
/* Expansion panel */
.expand{border:2px solid var(--g);border-radius:var(--r-lg);background:var(--gt);padding:14px;display:flex;gap:14px;position:relative;margin-top:10px;}
.expand-empty{border:2px solid var(--g);border-radius:var(--r-lg);background:var(--gt);padding:14px;position:relative;margin-top:10px;}
.exp-arrow-wrap{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;position:absolute;top:-8px;left:13px;right:13px;pointer-events:none;}
.exp-arrow{display:flex;justify-content:center;}
.exp-arr-shape{width:12px;height:12px;background:var(--gt);border-left:2px solid var(--g);border-top:2px solid var(--g);transform:rotate(45deg);}
.exp-arr-shape-e{background:var(--gt);}
.exp-left{flex:1;}
.exp-context{font-family:var(--fs);font-size:10px;font-weight:500;letter-spacing:.07em;text-transform:uppercase;color:var(--gd);margin-bottom:4px;}
.exp-name{font-family:var(--fd);font-size:22px;font-weight:300;line-height:1.25;}
.exp-meta{font-family:var(--fs);font-size:12px;color:var(--muted);margin-top:4px;}
.ing-wrap{display:flex;flex-wrap:wrap;gap:4px;margin-top:10px;}
.ing{font-family:var(--fs);font-size:10px;background:#fff;border:1px solid var(--border);border-radius:var(--r-full);padding:2px 8px;color:var(--text);}
.ing-s{background:var(--subtle);border-color:var(--subtle);color:var(--muted);}
.exp-badges{display:flex;gap:6px;margin-top:8px;flex-wrap:wrap;}
.exp-right{display:flex;flex-direction:column;gap:5px;width:118px;flex-shrink:0;}
.exp-btn{padding:7px;border-radius:var(--r-md);border:1px solid var(--border);background:#fff;font-family:var(--fs);font-size:11px;font-weight:500;text-align:center;cursor:pointer;letter-spacing:.04em;}
.exp-pri{background:var(--gd);color:#fff;border:none;}
.exp-err{color:var(--err);border-color:var(--err);background:transparent;}
/* Compact remaining below expansion */
.remaining{margin-top:10px;}
.rem-hd{font-family:var(--fs);font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);margin-bottom:6px;}
/* State 3: empty slot expansion */
.exp-sug-title{font-family:var(--fs);font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--gd);margin-bottom:10px;}
.exp-sug-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;}
.exp-sc{border:1px solid var(--gl);border-radius:var(--r-md);background:#fff;padding:10px;cursor:pointer;}
.exp-sc:hover{box-shadow:var(--sh-raised);}
.exp-sc-name{font-family:var(--fd);font-size:13px;font-weight:300;line-height:1.3;color:var(--text);}
.exp-sc-meta{font-family:var(--fs);font-size:10px;color:var(--muted);margin-top:3px;}
.exp-sc-tag{font-family:var(--fs);font-size:9px;font-weight:500;padding:2px 6px;border-radius:2px;background:var(--gt);color:var(--gd);margin-top:6px;display:inline-block;}
.exp-all{font-family:var(--fs);font-size:11px;font-weight:500;color:var(--yx);margin-top:10px;display:block;}
/* Right panel: idle hint */
.rp-idle{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:6px;padding:16px;}
.rp-idle-t{font-family:var(--fs);font-size:12px;color:var(--text);}
.rp-idle-s{font-family:var(--fs);font-size:11px;color:var(--muted);max-width:150px;line-height:1.5;}
/* Right panel: today card */
.today-card{background:var(--yt);border:1px solid var(--yl);border-radius:var(--r-md);padding:10px;margin-bottom:10px;}
.tc-label{font-family:var(--fs);font-size:9px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--yx);margin-bottom:4px;}
.tc-name{font-family:var(--fd);font-size:14px;font-weight:300;line-height:1.3;}
.tc-meta{font-family:var(--fs);font-size:10px;color:var(--muted);margin-top:2px;}
/* Right panel: picker mode */
.picker-search{display:flex;align-items:center;gap:6px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);padding:6px 9px;margin-bottom:9px;}
.ps-icon{font-size:11px;color:var(--muted);}
.ps-text{font-family:var(--fs);font-size:11px;color:var(--muted);}
.pick-item{display:flex;align-items:center;gap:7px;padding:7px 0;border-bottom:1px solid var(--subtle);cursor:pointer;}
.pick-item:last-child{border-bottom:none;}
.pick-name{font-family:var(--fd);font-size:12px;font-weight:300;flex:1;line-height:1.3;}
.pick-meta{font-family:var(--fs);font-size:10px;color:var(--muted);}
</style>
</head>
<body>
<p class="eyebrow">Mealplan · Planer · Konzept</p>
<h1 class="title">C + E Kombiniert — Drei Zustände</h1>
<p class="sub">
Der Hauptbereich unterhalb des Kalender-Grids zeigt immer nützlichen Inhalt — je nach Zustand.
Kein leerer Raum, kein Tab-Wechsel nötig. Das rechte Panel bleibt für den Rezept-Picker reserviert.
</p>
<div class="flow-legend">
<div class="fl-item"><div class="fl-dot" style="background:var(--muted);"></div>Zustand 1 — Kein Tag ausgewählt</div>
<div class="fl-item"><div class="fl-dot" style="background:var(--g);"></div>Zustand 2 — Tag mit Rezept angeklickt</div>
<div class="fl-item"><div class="fl-dot" style="background:var(--yx);"></div>Zustand 3 — Leerer Tag angeklickt</div>
</div>
<!-- ══════════════════════════════════════════════════════════════ -->
<!-- ZUSTAND 1: KEIN TAG AUSGEWÄHLT -->
<!-- ══════════════════════════════════════════════════════════════ -->
<div class="state-block">
<div class="state-label">
<span class="state-num">Zustand 1</span>
<span class="state-name">Kein Tag ausgewählt</span>
<span class="state-when">Standard beim Laden der Seite</span>
</div>
<div class="frame" style="height:670px;">
<div class="tb">
<span class="tb-h1">Wochenplaner</span>
<span class="tb-range">7.13. Apr</span>
<div class="tb-arr"></div><div class="tb-arr"></div>
<button class="tb-btn">Heute</button>
<button class="tb-btn tb-ml tb-pri">+ Gericht hinzufügen</button>
</div>
<div class="body">
<!-- Sidebar -->
<div class="sb">
<div class="score-box">
<div class="sb-lbl">Abwechslungs-Score</div>
<div style="display:flex;align-items:baseline;gap:4px;"><span class="sc-big">7.8</span><span class="sc-den">/10</span></div>
<div class="pbar"><div class="pb-fill" style="width:78%;"></div></div>
<div class="sr"><span class="sr-l">Protein</span><div class="sr-b"><div class="sr-f" style="width:80%;background:var(--g);"></div></div><span class="sr-v">8.0</span></div>
<div class="sr"><span class="sr-l">Zutaten</span><div class="sr-b"><div class="sr-f" style="width:72%;background:var(--y);"></div></div><span class="sr-v">7.2</span></div>
<div class="sr"><span class="sr-l">Aufwand</span><div class="sr-b"><div class="sr-f" style="width:82%;background:var(--g);"></div></div><span class="sr-v">8.2</span></div>
<a style="display:block;margin-top:8px;font-family:var(--fs);font-size:10px;font-weight:500;color:var(--yx);">Variety-Analyse →</a>
</div>
<div>
<div class="sb-lbl">Überschneidungen</div>
<div class="w-item">⚠ Hähnchen an Mo + Do</div>
<div class="w-item">⚠ Tomaten an Di + Do</div>
</div>
<div>
<div class="sb-lbl">Geplant</div>
<div style="display:flex;align-items:baseline;gap:3px;"><span style="font-family:var(--fd);font-size:20px;font-weight:300;">5</span><span style="font-family:var(--fs);font-size:10px;color:var(--muted);">/ 7 Tage</span></div>
<div class="dp" style="margin-top:5px;">
<div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--border);"></div><div class="dp-s" style="background:var(--border);"></div>
</div>
</div>
</div>
<!-- Main area -->
<div class="main">
<!-- Calendar grid: no selection -->
<div class="grid7">
<div><div class="d-abbr">Mo</div><div class="d-badge">7</div><div class="tile" style="height:86px;"><p class="tr">Hähnchen-Curry</p><p class="tm">35 Min</p><span class="tbdg b-m">mittel</span></div></div>
<div><div class="d-abbr">Di</div><div class="d-badge db-t">8</div><div class="tile tile-t" style="height:86px;"><p class="tr">Pasta Bolognese</p><p class="tm">45 Min</p><span class="tbdg b-m">mittel</span></div></div>
<div><div class="d-abbr">Mi</div><div class="d-badge">9</div><div class="tile" style="height:86px;"><p class="tr">Gemüse-Stir-fry</p><p class="tm">20 Min</p><span class="tbdg b-e">einfach</span></div></div>
<div><div class="d-abbr">Do</div><div class="d-badge">10</div><div class="tile" style="height:86px;"><p class="tr">Lachs mit Kartoffeln</p><p class="tm">30 Min</p><span class="tbdg b-e">einfach</span></div></div>
<div><div class="d-abbr">Fr</div><div class="d-badge">11</div><div class="tile" style="height:86px;"><p class="tr">Pizza Margherita</p><p class="tm">50 Min</p><span class="tbdg b-h">aufwändig</span></div></div>
<div><div class="d-abbr">Sa</div><div class="d-badge">12</div><div class="tile tile-e" style="height:86px;"><span style="font-size:16px;">+</span><span style="font-family:var(--fs);font-size:9px;">wählen</span></div></div>
<div><div class="d-abbr">So</div><div class="d-badge">13</div><div class="tile tile-e" style="height:86px;"><span style="font-size:16px;">+</span><span style="font-family:var(--fs);font-size:9px;">wählen</span></div></div>
</div>
<!-- ▼ BELOW GRID: Agenda (all 7 days) ▼ -->
<div style="margin-top:16px;">
<div class="sec-hd">
<span class="sec-t">Diese Woche</span>
</div>
<div class="agenda">
<div class="ag-item">
<div class="ag-day">Mo 7.4</div>
<div class="ag-name">Hähnchen-Curry</div>
<div class="ag-meta">35 Min · mittel</div>
</div>
<div class="ag-item ag-item-today">
<div class="ag-today-dot"></div>
<div class="ag-day" style="color:var(--yx);">Di 8.4</div>
<div class="ag-name">Pasta Bolognese</div>
<div class="ag-meta">45 Min · mittel</div>
</div>
<div class="ag-item">
<div class="ag-day">Mi 9.4</div>
<div class="ag-name">Gemüse-Stir-fry</div>
<div class="ag-meta">20 Min · einfach</div>
</div>
<div class="ag-item">
<div class="ag-day">Do 10.4</div>
<div class="ag-name">Lachs mit Kartoffeln</div>
<div class="ag-meta">30 Min · einfach</div>
</div>
<div class="ag-item">
<div class="ag-day">Fr 11.4</div>
<div class="ag-name">Pizza Margherita</div>
<div class="ag-meta">50 Min · aufwändig</div>
</div>
<div class="ag-item ag-t">
<div class="ag-day">Sa 12.4</div>
<div class="ag-name" style="font-family:var(--fs);font-size:12px;color:var(--muted);">Noch kein Gericht</div>
<div class="ag-action">+ Hinzufügen</div>
</div>
<div class="ag-item ag-t">
<div class="ag-day">So 13.4</div>
<div class="ag-name" style="font-family:var(--fs);font-size:12px;color:var(--muted);">Noch kein Gericht</div>
<div class="ag-action">+ Hinzufügen</div>
</div>
</div>
</div>
<!-- ▼ BELOW AGENDA: Suggestions for empty days ▼ -->
<div style="margin-top:18px;">
<div class="sec-hd">
<span class="sec-t">Vorschläge für ungeplante Tage</span>
<a class="sec-link">Alle Rezepte →</a>
</div>
<!-- Sa -->
<div class="sug-block">
<div class="sug-day-hd">
<span class="sug-day-name">Samstag, 12. Apr</span>
<span class="sug-empty-pill">kein Gericht</span>
</div>
<div class="sug-cards">
<div class="sc-item">
<div class="sc-name">Ramen mit Ei</div>
<div class="sc-meta">40 Min · mittel</div>
<span class="sc-tag">Neues Protein</span>
</div>
<div class="sc-item">
<div class="sc-name">Shakshuka</div>
<div class="sc-meta">25 Min · einfach</div>
<span class="sc-tag">Keine Überschneidung</span>
</div>
<div class="sc-item">
<div class="sc-name">Rindfleisch-Tacos</div>
<div class="sc-meta">30 Min · einfach</div>
<span class="sc-tag sc-tag-y">Aufwand: einfach</span>
</div>
</div>
</div>
<!-- So -->
<div class="sug-block" style="margin-top:14px;">
<div class="sug-day-hd">
<span class="sug-day-name">Sonntag, 13. Apr</span>
<span class="sug-empty-pill">kein Gericht</span>
</div>
<div class="sug-cards">
<div class="sc-item">
<div class="sc-name">Pho Bo</div>
<div class="sc-meta">60 Min · aufwändig</div>
<span class="sc-tag">Neues Protein</span>
</div>
<div class="sc-item">
<div class="sc-name">Lachs-Avocado-Bowl</div>
<div class="sc-meta">15 Min · einfach</div>
<span class="sc-tag">Keine Überschneidung</span>
</div>
<div class="sc-item">
<div class="sc-name">Kürbissuppe</div>
<div class="sc-meta">35 Min · einfach</div>
<span class="sc-tag">Keine Überschneidung</span>
</div>
</div>
</div>
</div>
</div>
<!-- Right panel: idle, shows today quick-start -->
<div class="rp">
<div class="today-card">
<div class="tc-label">Heute Abend</div>
<div class="tc-name">Pasta Bolognese</div>
<div class="tc-meta">Dienstag · 45 Min · mittel</div>
<button class="rp-btn rp-pri" style="margin-top:8px;padding:6px;">Koch-Modus starten</button>
</div>
<div class="hr"></div>
<div class="rp-idle" style="flex:1;">
<div class="rp-idle-t">Tag auswählen</div>
<div class="rp-idle-s">Klicke eine Kachel um Details zu sehen oder ein Gericht zu wählen</div>
</div>
</div>
</div>
</div>
<div class="note">
<strong>Inhalt ohne Klick:</strong> Agenda aller 7 Tage (geplante + leere), darunter Vorschläge für die beiden
ungeplanten Tage — anstelle von Score-Delta zeigen die Karten kurze Begründungs-Tags
(Neues Protein / Keine Überschneidung / Aufwand-Hinweis). Das rechte Panel zeigt „Heute Abend" als
direkte Koch-Modus-Abkürzung. Kein leerer Raum, kein Klick nötig.
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════ -->
<!-- ZUSTAND 2: TAG MIT REZEPT AUSGEWÄHLT (Mi) -->
<!-- ══════════════════════════════════════════════════════════════ -->
<div class="state-block">
<div class="state-label">
<span class="state-num">Zustand 2</span>
<span class="state-name">Tag mit Rezept angeklickt</span>
<span class="state-when">Klick auf Mi, 9. Apr → Gemüse-Stir-fry</span>
</div>
<div class="frame" style="height:680px;">
<div class="tb">
<span class="tb-h1">Wochenplaner</span>
<span class="tb-range">7.13. Apr</span>
<div class="tb-arr"></div><div class="tb-arr"></div>
<button class="tb-btn">Heute</button>
<button class="tb-btn tb-ml tb-pri">+ Gericht hinzufügen</button>
</div>
<div class="body">
<!-- Sidebar (same) -->
<div class="sb">
<div class="score-box">
<div class="sb-lbl">Abwechslungs-Score</div>
<div style="display:flex;align-items:baseline;gap:4px;"><span class="sc-big">7.8</span><span class="sc-den">/10</span></div>
<div class="pbar"><div class="pb-fill" style="width:78%;"></div></div>
<div class="sr"><span class="sr-l">Protein</span><div class="sr-b"><div class="sr-f" style="width:80%;background:var(--g);"></div></div><span class="sr-v">8.0</span></div>
<div class="sr"><span class="sr-l">Zutaten</span><div class="sr-b"><div class="sr-f" style="width:72%;background:var(--y);"></div></div><span class="sr-v">7.2</span></div>
<div class="sr"><span class="sr-l">Aufwand</span><div class="sr-b"><div class="sr-f" style="width:82%;background:var(--g);"></div></div><span class="sr-v">8.2</span></div>
<a style="display:block;margin-top:8px;font-family:var(--fs);font-size:10px;font-weight:500;color:var(--yx);">Variety-Analyse →</a>
</div>
<div>
<div class="sb-lbl">Überschneidungen</div>
<div class="w-item">⚠ Hähnchen an Mo + Do</div>
<div class="w-item">⚠ Tomaten an Di + Do</div>
</div>
<div>
<div class="sb-lbl">Geplant</div>
<div style="display:flex;align-items:baseline;gap:3px;"><span style="font-family:var(--fd);font-size:20px;font-weight:300;">5</span><span style="font-family:var(--fs);font-size:10px;color:var(--muted);">/ 7 Tage</span></div>
<div class="dp" style="margin-top:5px;">
<div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--border);"></div><div class="dp-s" style="background:var(--border);"></div>
</div>
</div>
</div>
<!-- Main area -->
<div class="main">
<!-- Calendar grid: Mi selected -->
<div class="grid7">
<div><div class="d-abbr">Mo</div><div class="d-badge">7</div><div class="tile" style="height:86px;"><p class="tr">Hähnchen-Curry</p><p class="tm">35 Min</p><span class="tbdg b-m">mittel</span></div></div>
<div><div class="d-abbr">Di</div><div class="d-badge db-t">8</div><div class="tile tile-t" style="height:86px;"><p class="tr">Pasta Bolognese</p><p class="tm">45 Min</p><span class="tbdg b-m">mittel</span></div></div>
<!-- Mi SELECTED -->
<div style="position:relative;">
<div class="d-abbr" style="color:var(--gd);">Mi</div>
<div class="d-badge db-s">9</div>
<div class="tile tile-s" style="height:86px;position:relative;">
<p class="tr">Gemüse-Stir-fry</p>
<p class="tm">20 Min</p>
<span class="tbdg b-e">einfach</span>
<div style="position:absolute;bottom:5px;left:50%;transform:translateX(-50%);font-size:8px;color:var(--gd);"></div>
</div>
</div>
<div><div class="d-abbr">Do</div><div class="d-badge">10</div><div class="tile" style="height:86px;opacity:.55;"><p class="tr">Lachs mit Kartoffeln</p><p class="tm">30 Min</p></div></div>
<div><div class="d-abbr">Fr</div><div class="d-badge">11</div><div class="tile" style="height:86px;opacity:.55;"><p class="tr">Pizza Margherita</p><p class="tm">50 Min</p></div></div>
<div><div class="d-abbr">Sa</div><div class="d-badge">12</div><div class="tile tile-e" style="height:86px;opacity:.55;"><span style="font-size:16px;">+</span></div></div>
<div><div class="d-abbr">So</div><div class="d-badge">13</div><div class="tile tile-e" style="height:86px;opacity:.55;"><span style="font-size:16px;">+</span></div></div>
</div>
<!-- ▼ EXPANSION: full-width, below grid, arrow to Mi (3rd column) ▼ -->
<div style="position:relative;margin-top:2px;">
<!-- Arrow pointing up to Mi tile (column 3 of 7) -->
<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:6px;position:absolute;top:-8px;left:0;right:0;pointer-events:none;">
<div></div><div></div>
<div class="exp-arrow"><div class="exp-arr-shape"></div></div>
<div></div><div></div><div></div><div></div>
</div>
<div class="expand">
<div class="exp-left">
<div class="exp-context">Mittwoch, 9. Apr · Abendessen</div>
<div class="exp-name">Gemüse-Stir-fry</div>
<div class="exp-meta">20 Min · einfach · 4 Portionen</div>
<div class="ing-wrap">
<span class="ing">Tofu</span>
<span class="ing">Paprika</span>
<span class="ing">Brokkoli</span>
<span class="ing">Karotten</span>
<span class="ing">Ingwer</span>
<span class="ing">Zucchini</span>
<span class="ing-s">Sesamöl</span>
<span class="ing-s">Sojasauce</span>
<span class="ing-s">Knoblauch</span>
</div>
<div class="exp-badges">
<span class="tbdg b-e" style="font-size:10px;padding:3px 7px;">einfach</span>
<span style="font-family:var(--fs);font-size:10px;font-weight:500;padding:3px 7px;border-radius:2px;background:var(--pt);color:var(--p);">Protein: Tofu</span>
<span style="font-family:var(--fs);font-size:10px;font-weight:500;padding:3px 7px;border-radius:2px;background:var(--gt);color:var(--gd);">Score ▲ +0.4</span>
</div>
</div>
<div class="exp-right">
<button class="exp-btn exp-pri">Koch-Modus</button>
<button class="exp-btn">Rezept ansehen</button>
<button class="exp-btn">Gericht tauschen</button>
<button class="exp-btn exp-err">Entfernen</button>
</div>
</div>
</div>
<!-- ▼ REMAINING WEEK below expansion ▼ -->
<div class="remaining">
<div class="rem-hd">Restliche Woche</div>
<div class="agenda">
<div class="ag-item">
<div class="ag-day">Do 10.4</div>
<div class="ag-name">Lachs mit Kartoffeln</div>
<div class="ag-meta">30 Min · einfach</div>
</div>
<div class="ag-item">
<div class="ag-day">Fr 11.4</div>
<div class="ag-name">Pizza Margherita</div>
<div class="ag-meta">50 Min · aufwändig</div>
</div>
<div class="ag-item ag-t">
<div class="ag-day">Sa 12.4</div>
<div class="ag-name" style="font-family:var(--fs);font-size:12px;color:var(--muted);">Noch kein Gericht</div>
<div class="ag-action">+ Hinzufügen</div>
</div>
<div class="ag-item ag-t">
<div class="ag-day">So 13.4</div>
<div class="ag-name" style="font-family:var(--fs);font-size:12px;color:var(--muted);">Noch kein Gericht</div>
<div class="ag-action">+ Hinzufügen</div>
</div>
</div>
</div>
</div>
<!-- Right panel: score context for selected day -->
<div class="rp">
<div class="rp-lbl">Mittwoch, 9. Apr</div>
<div style="font-family:var(--fs);font-size:11px;color:var(--muted);margin-bottom:10px;">Wie wirkt dieses Gericht?</div>
<div style="display:flex;align-items:baseline;gap:4px;margin-bottom:4px;">
<span style="font-family:var(--fd);font-size:22px;font-weight:300;">7.8</span>
<span style="font-family:var(--fs);font-size:11px;color:var(--muted);">/10</span>
<span style="font-family:var(--fs);font-size:11px;color:var(--gd);font-weight:500;margin-left:6px;">▲ +0.4</span>
</div>
<div class="pbar pbt" style="margin-bottom:12px;"><div class="pb-fill pbg" style="width:78%;"></div></div>
<div class="hr"></div>
<div style="font-family:var(--fs);font-size:10px;font-weight:500;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);margin-bottom:8px;">Dieses Gericht</div>
<div style="display:flex;flex-direction:column;gap:5px;">
<div style="display:flex;align-items:center;gap:6px;font-family:var(--fs);font-size:11px;color:var(--gd);"><span></span><span>Kein Protein-Overlap</span></div>
<div style="display:flex;align-items:center;gap:6px;font-family:var(--fs);font-size:11px;color:var(--gd);"><span></span><span>Neue Zutaten (Tofu, Paprika)</span></div>
<div style="display:flex;align-items:center;gap:6px;font-family:var(--fs);font-size:11px;color:var(--yx);"><span>~</span><span>Tofu 2× diese Woche</span></div>
</div>
</div>
</div>
</div>
<div class="note">
<strong>Nach Klick auf Mittwoch:</strong> Expansion öffnet sich direkt unter dem Grid mit Pfeil-Indikator.
Enthält Rezeptname groß, Zutaten-Tags (normale vs. Grundzutaten <span style="opacity:.6;">gedimmt</span>),
Score-Impact-Badge und alle Aktionen. Unselektierte Kacheln werden leicht ausgeblendet (opacity 55%).
Darunter: kompakte Liste der restlichen Woche — geplante Tage + leere Tage mit „+ Hinzufügen".
Das rechte Panel wechselt auf einen Variety-Kontext: zeigt was dieses Gericht zur Woche beiträgt (positiv/neutral/negativ).
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════ -->
<!-- ZUSTAND 3: LEERER TAG AUSGEWÄHLT (Sa) -->
<!-- ══════════════════════════════════════════════════════════════ -->
<div class="state-block">
<div class="state-label">
<span class="state-num">Zustand 3</span>
<span class="state-name">Leerer Tag angeklickt</span>
<span class="state-when">Klick auf Sa, 12. Apr → kein Gericht</span>
</div>
<div class="frame" style="height:680px;">
<div class="tb">
<span class="tb-h1">Wochenplaner</span>
<span class="tb-range">7.13. Apr</span>
<div class="tb-arr"></div><div class="tb-arr"></div>
<button class="tb-btn">Heute</button>
<button class="tb-btn tb-ml tb-pri">+ Gericht hinzufügen</button>
</div>
<div class="body">
<!-- Sidebar -->
<div class="sb">
<div class="score-box">
<div class="sb-lbl">Abwechslungs-Score</div>
<div style="display:flex;align-items:baseline;gap:4px;"><span class="sc-big">7.8</span><span class="sc-den">/10</span></div>
<div class="pbar"><div class="pb-fill" style="width:78%;"></div></div>
<div class="sr"><span class="sr-l">Protein</span><div class="sr-b"><div class="sr-f" style="width:80%;background:var(--g);"></div></div><span class="sr-v">8.0</span></div>
<div class="sr"><span class="sr-l">Zutaten</span><div class="sr-b"><div class="sr-f" style="width:72%;background:var(--y);"></div></div><span class="sr-v">7.2</span></div>
<div class="sr"><span class="sr-l">Aufwand</span><div class="sr-b"><div class="sr-f" style="width:82%;background:var(--g);"></div></div><span class="sr-v">8.2</span></div>
<a style="display:block;margin-top:8px;font-family:var(--fs);font-size:10px;font-weight:500;color:var(--yx);">Variety-Analyse →</a>
</div>
<div>
<div class="sb-lbl">Überschneidungen</div>
<div class="w-item">⚠ Hähnchen an Mo + Do</div>
<div class="w-item">⚠ Tomaten an Di + Do</div>
</div>
<div>
<div class="sb-lbl">Geplant</div>
<div style="display:flex;align-items:baseline;gap:3px;"><span style="font-family:var(--fd);font-size:20px;font-weight:300;">5</span><span style="font-family:var(--fs);font-size:10px;color:var(--muted);">/ 7 Tage</span></div>
<div class="dp" style="margin-top:5px;">
<div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--g);"></div><div class="dp-s" style="background:var(--border);"></div><div class="dp-s" style="background:var(--border);"></div>
</div>
</div>
</div>
<!-- Main area -->
<div class="main">
<!-- Calendar grid: Sa selected (empty) -->
<div class="grid7">
<div><div class="d-abbr">Mo</div><div class="d-badge">7</div><div class="tile" style="height:86px;opacity:.55;"><p class="tr">Hähnchen-Curry</p><p class="tm">35 Min</p></div></div>
<div><div class="d-abbr">Di</div><div class="d-badge db-t">8</div><div class="tile tile-t" style="height:86px;opacity:.55;"><p class="tr">Pasta Bolognese</p><p class="tm">45 Min</p></div></div>
<div><div class="d-abbr">Mi</div><div class="d-badge">9</div><div class="tile" style="height:86px;opacity:.55;"><p class="tr">Gemüse-Stir-fry</p><p class="tm">20 Min</p></div></div>
<div><div class="d-abbr">Do</div><div class="d-badge">10</div><div class="tile" style="height:86px;opacity:.55;"><p class="tr">Lachs mit Kartoffeln</p><p class="tm">30 Min</p></div></div>
<div><div class="d-abbr">Fr</div><div class="d-badge">11</div><div class="tile" style="height:86px;opacity:.55;"><p class="tr">Pizza Margherita</p><p class="tm">50 Min</p></div></div>
<!-- Sa: SELECTED EMPTY -->
<div style="position:relative;">
<div class="d-abbr" style="color:var(--gd);">Sa</div>
<div class="d-badge db-s">12</div>
<div class="tile tile-e tile-e-s" style="height:86px;">
<span style="font-size:20px;color:var(--gd);">+</span>
<span style="font-family:var(--fs);font-size:9px;color:var(--gd);">wählen</span>
<div style="position:absolute;bottom:5px;left:50%;transform:translateX(-50%);font-size:8px;color:var(--gd);"></div>
</div>
</div>
<div><div class="d-abbr">So</div><div class="d-badge">13</div><div class="tile tile-e" style="height:86px;opacity:.55;"><span style="font-size:16px;">+</span></div></div>
</div>
<!-- ▼ EXPANSION: suggestion cards for Sa ▼ -->
<div style="position:relative;margin-top:2px;">
<!-- Arrow to Sa (column 6) -->
<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:6px;position:absolute;top:-8px;left:0;right:0;pointer-events:none;">
<div></div><div></div><div></div><div></div><div></div>
<div class="exp-arrow"><div class="exp-arr-shape exp-arr-shape-e"></div></div>
<div></div>
</div>
<div class="expand-empty">
<div class="exp-sug-title">Vorschläge für Samstag, 12. Apr</div>
<div class="exp-sug-cards">
<div class="exp-sc">
<div class="exp-sc-name">Ramen mit Ei</div>
<div class="exp-sc-meta">40 Min · mittel</div>
<span class="exp-sc-tag">Neues Protein</span>
</div>
<div class="exp-sc">
<div class="exp-sc-name">Shakshuka</div>
<div class="exp-sc-meta">25 Min · einfach</div>
<span class="exp-sc-tag">Keine Überschneidung</span>
</div>
<div class="exp-sc">
<div class="exp-sc-name">Rindfleisch-Tacos</div>
<div class="exp-sc-meta">30 Min · einfach</div>
<span class="exp-sc-tag">Keine Überschneidung</span>
</div>
<div class="exp-sc">
<div class="exp-sc-name">Kürbissuppe</div>
<div class="exp-sc-meta">35 Min · einfach</div>
<span class="exp-sc-tag" style="background:var(--yt);color:var(--yx);">Aufwand: einfach</span>
</div>
<div class="exp-sc">
<div class="exp-sc-name">Tofu-Teriyaki</div>
<div class="exp-sc-meta">30 Min · einfach</div>
<span class="exp-sc-tag" style="background:var(--yt);color:var(--yx);">Gleiche Zutaten</span>
</div>
<!-- Alle Rezepte card -->
<div class="sc-add" style="min-height:auto;">Alle Rezepte →</div>
</div>
</div>
</div>
<!-- ▼ REMAINING WEEK below ▼ -->
<div class="remaining">
<div class="rem-hd">Noch diese Woche</div>
<div class="agenda">
<div class="ag-item">
<div class="ag-day">Mo 7.4</div>
<div class="ag-name">Hähnchen-Curry</div>
<div class="ag-meta">35 Min · mittel</div>
</div>
<div class="ag-item ag-item-today">
<div class="ag-today-dot"></div>
<div class="ag-day" style="color:var(--yx);">Di 8.4</div>
<div class="ag-name">Pasta Bolognese</div>
<div class="ag-meta">45 Min</div>
</div>
<div class="ag-item ag-t">
<div class="ag-day">So 13.4</div>
<div class="ag-name" style="font-family:var(--fs);font-size:12px;color:var(--muted);">Noch kein Gericht</div>
<div class="ag-action">+ Hinzufügen</div>
</div>
</div>
</div>
</div>
<!-- Right panel: full recipe picker (search + list) -->
<div class="rp">
<div class="rp-lbl">Samstag, 12. Apr</div>
<div class="picker-search">
<span class="ps-icon"></span>
<span class="ps-text">Rezept suchen…</span>
</div>
<div style="overflow-y:auto;flex:1;">
<div class="pick-item">
<div style="flex:1;"><div class="pick-name">Ramen mit Ei</div><div class="pick-meta">40 Min · mittel</div></div>
<span style="font-family:var(--fm);font-size:9px;background:var(--gt);color:var(--gd);padding:2px 5px;border-radius:2px;">Top</span>
</div>
<div class="pick-item">
<div style="flex:1;"><div class="pick-name">Shakshuka</div><div class="pick-meta">25 Min · einfach</div></div>
<span style="font-family:var(--fm);font-size:9px;background:var(--gt);color:var(--gd);padding:2px 5px;border-radius:2px;">Top</span>
</div>
<div class="pick-item">
<div style="flex:1;"><div class="pick-name">Kürbissuppe</div><div class="pick-meta">35 Min · einfach</div></div>
</div>
<div class="pick-item">
<div style="flex:1;"><div class="pick-name">Tofu-Teriyaki</div><div class="pick-meta">30 Min · einfach</div></div>
</div>
<div class="pick-item">
<div style="flex:1;"><div class="pick-name">Gemüse-Curry</div><div class="pick-meta">40 Min · mittel</div></div>
</div>
<div class="pick-item">
<div style="flex:1;"><div class="pick-name">Linseneintopf</div><div class="pick-meta">50 Min · einfach</div></div>
</div>
<div class="pick-item">
<div style="flex:1;"><div class="pick-name">Ofen-Lachs</div><div class="pick-meta">35 Min · einfach</div></div>
</div>
</div>
</div>
</div>
</div>
<div class="note">
<strong>Nach Klick auf leeren Samstag:</strong> Expansion zeigt 5 Vorschläge (3×3 Grid + „Alle Rezepte" als letztes Feld)
mit Begründungs-Tags statt Score-Delta. Die Farbkodierung der Tags macht die Qualität der Empfehlung lesbar ohne Zahlenwert:
Grün = klar besser, Gelb = neutral/tradeoff. Das rechte Panel öffnet gleichzeitig den vollständigen Rezept-Picker mit Suche —
für Nutzer die keinen der Vorschläge wollen. Klick auf eine Karte (main) oder Picker-Item (rechts) führt dasselbe aus: Rezept eintragen.
<br><br>
<strong>Tofu-Teriyaki</strong> erscheint mit „Gleiche Zutaten" (gelb) — das ist die sinnvolle Alternative zu einem Score-Delta von 0.2:
nicht versteckt, aber klar als Abwägung markiert.
</div>
</div>
</body>
</html>