Files
mealprep/specs/planner-redesign-flip-tiles.html
Marcel Raddatz f139dce82c docs(specs): add planner desktop redesign spec — flip tiles
Final design spec for the planner desktop layout overhaul:
full-bleed color tiles, CSS 3D card flip for recipe detail,
no persistent right panel, inline suggestions on empty days.
Includes interactive mockup and written component spec.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 18:20:01 +02:00

460 lines
27 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Planner Redesign — Flip Tiles · Final Spec</title>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,400;9..144,500&family=DM+Sans:wght@300;400;500;600&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet"/>
<!--
spec:agent
document: Planner Desktop Redesign — Flip Tiles
version: 1.0
route: /planner (desktop)
screens: Planner main area — tile grid, sidebar, recipe picker drawer
key-decisions:
- Full-bleed color/image tiles (no blank body space)
- CSS 3D card flip replaces expansion panel
- No persistent right panel — tiles fill full remaining width
- Ingredient/cuisine color palette as heroImageUrl fallback
- Inline suggestions on empty tiles (reasoning tags, no delta numbers)
- No "Gericht hinzufügen" toolbar button (empty tile CTA handles it)
- Recipe picker opens as slide-in drawer (on demand only)
last-updated: 2026-04
reference-mockups:
- specs/planner-flip-tiles.html (interactive demo, color palette)
-->
<style>
:root {
--color-page: #FAFAF7;
--color-surface: #F5F4EE;
--color-subtle: #EDECEA;
--color-border: #D8D7D0;
--color-text-muted: #6B6A63;
--color-text: #1C1C18;
--green-tint: #E8F5EA;
--green-light: #AEDCB0;
--green: #3D8C4A;
--green-dark: #2E6E39;
--yellow-tint: #FDF6D8;
--yellow-light: #F9E08A;
--yellow: #F2C12E;
--yellow-text: #8A6800;
--color-error: #DC4C3E;
--font-display: 'Fraunces', Georgia, serif;
--font-sans: 'DM Sans', system-ui, sans-serif;
--font-mono: 'DM Mono', monospace;
--radius-sm: 4px; --radius-md: 6px; --radius-lg: 10px; --radius-full: 9999px;
--shadow-card: 0 1px 3px rgba(28,28,24,.06), 0 1px 2px rgba(28,28,24,.04);
--shadow-raised: 0 4px 12px rgba(28,28,24,.10), 0 2px 4px rgba(28,28,24,.06);
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: var(--font-sans); background: var(--color-page); color: var(--color-text); font-size: 14px; line-height: 1.6; }
.doc { max-width: 900px; margin: 0 auto; padding: 48px 40px 96px; }
.doc-header { display: flex; justify-content: space-between; align-items: flex-end; padding-bottom: 28px; border-bottom: 1px solid var(--color-border); margin-bottom: 48px; }
.doc-header h1 { font-family: var(--font-display); font-size: 26px; font-weight: 500; letter-spacing: -0.02em; margin-bottom: 4px; }
.doc-header p { font-size: 13px; color: var(--color-text-muted); }
.doc-meta { font-family: var(--font-mono); font-size: 11px; color: var(--color-text-muted); text-align: right; line-height: 1.9; }
.intro { font-size: 14px; line-height: 1.75; color: var(--color-text); max-width: 700px; margin-bottom: 48px; }
.intro p + p { margin-top: 12px; }
.section { margin-bottom: 56px; }
.section-label { font-size: 10px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-text-muted); padding-bottom: 10px; border-bottom: 1px solid var(--color-border); margin-bottom: 28px; }
h2 { font-family: var(--font-display); font-size: 20px; font-weight: 400; margin-bottom: 14px; }
h3 { font-size: 13px; font-weight: 600; margin-bottom: 8px; color: var(--color-text); }
p { margin-bottom: 10px; font-size: 14px; line-height: 1.7; }
ul { padding-left: 20px; margin-bottom: 12px; }
li { font-size: 14px; line-height: 1.65; margin-bottom: 4px; }
code { font-family: var(--font-mono); font-size: 12px; background: var(--color-subtle); border-radius: 3px; padding: 1px 5px; }
pre { font-family: var(--font-mono); font-size: 12px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 14px 16px; margin: 12px 0; overflow-x: auto; line-height: 1.6; }
.callout { background: var(--color-surface); border-left: 3px solid var(--color-border); border-radius: 0 var(--radius-md) var(--radius-md) 0; padding: 12px 16px; margin: 16px 0; font-size: 13px; line-height: 1.65; }
.callout.green { border-color: var(--green); background: var(--green-tint); }
.callout.yellow { border-color: var(--yellow); background: var(--yellow-tint); }
.callout strong { font-weight: 600; }
table { width: 100%; border-collapse: collapse; font-size: 13px; margin: 16px 0; }
th { font-size: 10px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-text-muted); padding: 8px 12px; text-align: left; border-bottom: 2px solid var(--color-border); }
td { padding: 9px 12px; border-bottom: 1px solid var(--color-subtle); vertical-align: top; }
tr:last-child td { border-bottom: none; }
.swatch-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0; }
.swatch { width: 80px; border-radius: var(--radius-md); overflow: hidden; box-shadow: var(--shadow-card); }
.swatch-color { height: 44px; }
.swatch-name { font-size: 10px; font-weight: 500; padding: 4px 6px; background: var(--color-page); border-top: 1px solid var(--color-border); }
.swatch-sub { font-size: 9px; color: var(--color-text-muted); padding: 0 6px 4px; }
.state-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; margin: 16px 0; }
.state-card { border: 1px solid var(--color-border); border-radius: var(--radius-lg); padding: 14px 16px; }
.state-name { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-text-muted); margin-bottom: 6px; }
.state-desc { font-size: 13px; line-height: 1.6; }
.component-row { display: flex; gap: 8px; align-items: baseline; padding: 10px 0; border-bottom: 1px solid var(--color-subtle); }
.component-row:last-child { border-bottom: none; }
.comp-file { font-family: var(--font-mono); font-size: 12px; color: var(--color-text); flex: 0 0 auto; min-width: 280px; }
.comp-action { font-size: 13px; color: var(--color-text-muted); }
.badge { display: inline-block; font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: var(--radius-full); }
.badge-new { background: var(--green-tint); color: var(--green-dark); }
.badge-mod { background: var(--yellow-tint); color: var(--yellow-text); }
.badge-del { background: #fde8e8; color: var(--color-error); }
</style>
</head>
<body>
<div class="doc">
<div class="doc-header">
<div>
<h1>Planner Desktop Redesign</h1>
<p>Flip Tiles · Final Spec · Route: <code>/planner</code></p>
</div>
<div class="doc-meta">
Version 1.0<br>
2026-04<br>
Mockup: <code>specs/planner-flip-tiles.html</code>
</div>
</div>
<div class="intro">
<p>
Der Wochenplaner hat auf Desktop aktuell ~80 % vertikalen Leerraum unterhalb des 7-Spalten-Kalenders.
Zusätzlich ist das rechte Panel im Leerlauf nicht genutzt. Dieses Spec beschreibt ein vollständiges
Redesign der Desktop-Hauptfläche: Die Kacheln füllen die volle Höhe und Breite, Rezeptdetails werden
über einen CSS-3D-Flip direkt in der Kachel angezeigt, und leere Tage zeigen Inline-Vorschläge.
</p>
<p>
Das rechte Panel entfällt dauerhaft. Der Rezept-Picker öffnet sich als Slide-in-Drawer ausschließlich
auf Anfrage (Aktion „Gericht tauschen" auf der Kachel-Rückseite). Der Toolbar-Button
„Gericht hinzufügen" entfällt, da jede leere Kachel eine eigene CTA hat.
</p>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-label">01 · Layout</div>
<h2>Seitenstruktur</h2>
<p>Desktop-Layout: 2 Spalten. Kein persistentes rechtes Panel mehr.</p>
<pre>┌─────────────────────────────────────────────────────────────┐
│ Toolbar (Wochenplaner · 7.13. Apr Heute) │
├──────────┬──────────────────────────────────────────────────┤
│ Sidebar │ 7-Spalten-Kachelgrid (flex: 1, height: 100%) │
│ 184 px │ │
│ Variety │ Mo Di Mi Do Fr Sa So │
│ Score │ ████ ████ ████ ████ ████ ░░░░ ░░░░ │
│ │ ████ ████ ████ ████ ████ ░+░░ ░+░░ │
│ │ ████ ████ ████ ████ ████ ░Vor░ ░Vor░ │
└──────────┴──────────────────────────────────────────────────┘</pre>
<ul>
<li><strong>Sidebar (184 px, flex-shrink: 0):</strong> Variety-Score-Card, Sub-Scores, Überschneidungs-Warnungen, Link zur Variety-Analyse. Unverändert.</li>
<li><strong>Main (flex: 1):</strong> <code>display: grid; grid-template-columns: repeat(7, 1fr); gap: 7px; height: 100%</code>. Kacheln füllen die gesamte verbleibende Breite und Höhe.</li>
<li><strong>Toolbar:</strong> Nur Navigation — Wochenbezeichnung, Zurück/Vor-Pfeile, Heute-Button. Kein „+ Gericht hinzufügen" mehr.</li>
</ul>
<div class="callout yellow">
<strong>Entfernt:</strong> Das rechte Panel (<code>width: 228px</code>) mit der „Heute Abend"-Karte und dem Leerlauf-Hinweis entfällt vollständig. Koch-Modus ist auf der Kachel-Rückseite zugänglich.
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-label">02 · Kachel-Zustände</div>
<h2>Tile States</h2>
<div class="state-grid">
<div class="state-card">
<div class="state-name">Standard (gefüllt)</div>
<div class="state-desc">
Vollbild-Farbhintergrund (Gradient nach Zutat/Küche) oder <code>heroImageUrl</code>.
Dual-Gradient-Overlay (oben + unten dunkel, Mitte klar).
Oben: Tageskürzel + Datumsziffer. Unten: Rezeptname, Kochzeit, Tags.
<br><br>
<code>box-shadow: var(--sh-card)</code> — kein sichtbarer Ring.
</div>
</div>
<div class="state-card">
<div class="state-name">Heute (gefüllt)</div>
<div class="state-desc">
Identisch wie Standard, aber mit gelbem Ring via
<code>box-shadow: 0 0 0 2px var(--yellow), var(--sh-card)</code>.
Datumsziffer-Badge in <code>--yellow</code>. Tag-Label „Heute" zusätzlich als frosted Tag.
</div>
</div>
<div class="state-card">
<div class="state-name">Ausgewählt / Geflippt</div>
<div class="state-desc">
Grüner Ring: <code>box-shadow: 0 0 0 2px var(--green), var(--sh-raised)</code>.
Karte dreht sich 180° (CSS 3D, siehe §04). Alle anderen Kacheln werden auf 38 % Deckkraft
gedimmt und sind nicht klickbar.
</div>
</div>
<div class="state-card">
<div class="state-name">Leer</div>
<div class="state-desc">
Kein Flip. Gestrichelter Rahmen (<code>border: 1.5px dashed var(--color-border)</code>),
<code>background: var(--color-surface)</code>. Oben: Tageskürzel + Datum.
Darunter: <code>+</code> Icon + „Gericht wählen". Rest der Kachel: Inline-Vorschläge (§05).
</div>
</div>
</div>
<div class="callout">
<strong>box-shadow statt border:</strong> Statusringe werden via <code>box-shadow</code> gesetzt, nicht via <code>border</code>,
um Layout-Shift zu vermeiden. Die Kacheln behalten identische Außenmaße in allen Zuständen.
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-label">03 · Farb-Palette</div>
<h2>Ingredient &amp; Cuisine Colors</h2>
<p>
Wenn <code>heroImageUrl</code> vorhanden ist, wird das echte Foto als <code>background-image</code> gesetzt.
Fehlt es, greift die folgende Prioritätskette:
</p>
<ol style="padding-left:20px;margin-bottom:16px;">
<li>Ersten Tag mit <code>tagType = "protein"</code> finden → Protein-Farbe</li>
<li>Ersten Tag mit <code>tagType = "cuisine"</code> finden → Küchenstil-Farbe</li>
<li>Fallback: <code>background: var(--color-surface)</code> (neutral)</li>
</ol>
<h3>Protein-Farben</h3>
<div class="swatch-row">
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#d4923a,#a85e1a,#7a3d0c)"></div><div class="swatch-name">Hähnchen</div><div class="swatch-sub">protein-haehnchen</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#c04545,#8b2020,#5a1010)"></div><div class="swatch-name">Rind</div><div class="swatch-sub">protein-rind</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#5b9fd4,#2868a0,#10406e)"></div><div class="swatch-name">Fisch</div><div class="swatch-sub">protein-fisch</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#5fa85e,#2e7031,#1a4a1e)"></div><div class="swatch-name">Tofu</div><div class="swatch-sub">protein-tofu</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#7bc47b,#3d8c3d,#1e5a1e)"></div><div class="swatch-name">Vegetarisch</div><div class="swatch-sub">protein-veg</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#d4785a,#a04535,#6e2418)"></div><div class="swatch-name">Schwein</div><div class="swatch-sub">protein-schwein</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#9e6b3a,#6b3f1a,#3e2208)"></div><div class="swatch-name">Lamm</div><div class="swatch-sub">protein-lamm</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#d4b832,#a07010,#6e4800)"></div><div class="swatch-name">Ei</div><div class="swatch-sub">protein-ei</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#8b6b3a,#5e421a,#3a2408)"></div><div class="swatch-name">Hülsen­früchte</div><div class="swatch-sub">protein-huelsenfruechte</div></div>
</div>
<h3>Küchenstil-Farben</h3>
<div class="swatch-row">
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#c04545,#7a1e1e,#4a0f0f)"></div><div class="swatch-name">Italienisch</div><div class="swatch-sub">cuisine-italienisch</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#3a6e3a,#1e4a1e,#0e2e0e)"></div><div class="swatch-name">Asiatisch</div><div class="swatch-sub">cuisine-asiatisch</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#c49010,#8b5e00,#5a3800)"></div><div class="swatch-name">Indisch</div><div class="swatch-sub">cuisine-indisch</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#d4923a,#8b4e10,#5a2e00)"></div><div class="swatch-name">Mexikanisch</div><div class="swatch-sub">cuisine-mexikanisch</div></div>
<div class="swatch"><div class="swatch-color" style="background:linear-gradient(160deg,#4a90b8,#1e5a8b,#0a3456)"></div><div class="swatch-name">Mediterran</div><div class="swatch-sub">cuisine-mediterran</div></div>
</div>
<p>
Die CSS-Klassen (<code>protein-haehnchen</code>, <code>cuisine-asiatisch</code>, …) werden
serverseitig aus den Rezept-Tags abgeleitet und als Svelte-Prop übergeben, z.B.
<code>colorClass="protein-haehnchen"</code>. Das Component setzt die Klasse auf dem Kachel-Wrapper.
</p>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-label">04 · Flip-Mechanik</div>
<h2>CSS 3D Card Flip</h2>
<p>Jede gefüllte Kachel besteht aus drei verschachtelten Elementen:</p>
<pre>.scene → perspective: 900px; border-radius: var(--radius-lg); cursor: pointer
.card → position: relative; transform-style: preserve-3d
transition: transform .45s cubic-bezier(.4,0,.2,1)
.card.flipped → transform: rotateY(180deg)
.card-front → backface-visibility: hidden; position: absolute; inset: 0
.card-back → backface-visibility: hidden; transform: rotateY(180deg)
position: absolute; inset: 0; background: var(--color-page)</pre>
<h3>Vorderseite</h3>
<ul>
<li>Vollbild-Farbe oder <code>background-image: url(heroImageUrl)</code> mit <code>background-size: cover</code></li>
<li>Dual-Gradient-Overlay als absolutes <code>::after</code>-Pseudo-Element:<br>
<code>linear-gradient(to bottom, rgba(0,0,0,.38) 0%, transparent 28%, transparent 48%, rgba(0,0,0,.62) 100%)</code></li>
<li>Oben links: Tageskürzel (9px uppercase). Oben rechts: Datums-Badge (Kreis)</li>
<li>Unten: Rezeptname (Fraunces 13px), Meta-Zeile (Kochzeit · Aufwand), Tag-Chips</li>
</ul>
<h3>Rückseite</h3>
<ul>
<li><strong>Farbstreifen (5 px)</strong> oben — identischer Gradient wie die Vorderseite. Gibt visuelle Kontinuität.</li>
<li>Tageskürzel + Datum (links) · × Schließen-Button (rechts)</li>
<li>Rezeptname (Fraunces 15px)</li>
<li>Meta: Kochzeit · Aufwand · Portionen</li>
<li>Zutaten-Pills: normale Zutaten als <code>.ingredient</code>, Vorrats-Zutaten (Staples) gedimmt als <code>.ingredient--staple</code></li>
<li>Aktionen (gestapelt, volle Breite):</li>
</ul>
<table>
<thead><tr><th>Aktion</th><th>Stil</th><th>Verhalten</th></tr></thead>
<tbody>
<tr><td>Koch-Modus starten</td><td>Primary (grün ausgefüllt)</td><td>Navigiert zu <code>/planner/cook/[slotId]</code></td></tr>
<tr><td>Rezept ansehen</td><td>Secondary (Rahmen)</td><td>Navigiert zu <code>/recipes/[recipeId]</code></td></tr>
<tr><td>Gericht tauschen</td><td>Secondary (Rahmen)</td><td>Öffnet Rezept-Picker-Drawer (§06)</td></tr>
<tr><td>Entfernen</td><td>Danger (roter Text, transparenter BG)</td><td>Löscht den Slot, Kachel wird leer</td></tr>
</tbody>
</table>
<h3>Interaction Flow</h3>
<ul>
<li>Klick auf <code>.scene</code><code>.card.classList.toggle('flipped')</code></li>
<li>Alle Geschwister-Kacheln im Grid → <code>opacity: 0.38; pointer-events: none</code></li>
<li>× Button auf Rückseite → <code>event.stopPropagation()</code>, <code>classList.remove('flipped')</code>, Geschwister-Opacity zurücksetzen</li>
<li>Escape-Taste → aktive Kachel zurückdrehen</li>
</ul>
<div class="callout green">
<strong>Kein API-Aufruf beim Flip.</strong> Alle dargestellten Daten (Name, Zutaten, Aktionen) sind bereits
im vorhandenen <code>slotMap</code>-State vorhanden. Der Flip ist eine rein visuelle Operation.
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-label">05 · Leere Kacheln</div>
<h2>Empty Tile — Inline Suggestions</h2>
<p>Leere Kacheln haben denselben <code>height: 100%</code> wie gefüllte Kacheln. Kein Flip.</p>
<pre>┌─────────────────┐
│ Sa 12 │ ← Tageskürzel + Datum
│─────────────────│
│ + │
│ Gericht wählen │ ← Klick öffnet Rezept-Picker-Drawer
│─────────────────│
│ VORSCHLÄGE │
│ Ramen mit Ei [Neues Protein] │
│ Shakshuka [Kein Overlap] │
│ Tacos [Aufwand: leicht]│
│ │
│ Alle Rezepte → │
└────────────────────────────────┘</pre>
<h3>Vorschlag-Tags (Reasoning)</h3>
<p>Anstelle numerischer Score-Deltas (die für leere Slots immer positiv sind und daher keine Information tragen)
werden Begründungs-Tags angezeigt:</p>
<table>
<thead><tr><th>Tag</th><th>Farbe</th><th>Bedeutung</th></tr></thead>
<tbody>
<tr><td>Neues Protein</td><td>Grün</td><td>Proteinquelle kommt diese Woche noch nicht vor</td></tr>
<tr><td>Kein Overlap</td><td>Grün</td><td>Keine Zutaten-Überschneidung mit anderen Tagen</td></tr>
<tr><td>Aufwand: leicht</td><td>Gelb</td><td>Kochzeit &lt; 30 Min oder Aufwand = einfach</td></tr>
<tr><td>Aufwand: mittel</td><td>Neutral</td><td>Mittlerer Aufwand</td></tr>
</tbody>
</table>
<div class="callout">
<strong>Datenquelle:</strong> Die vorhandene <code>GET /api/suggestions?weekId=&amp;dayOfWeek=</code> API liefert
<code>SuggestionItem { recipe, scoreDelta, hasConflict }</code>. Die Reasoning-Tags werden frontend-seitig
aus den Rezept-Tags und dem vorhandenen <code>slotMap</code> abgeleitet, kein Backend-Änderungsbedarf.
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-label">06 · Rezept-Picker</div>
<h2>Recipe Picker Drawer</h2>
<p>
Der Rezept-Picker öffnet sich als Slide-in-Drawer von rechts — ausschließlich auf explizite Anfrage.
Er hat keinen persistenten Platz im Layout mehr.
</p>
<h3>Trigger</h3>
<ul>
<li>Klick auf <strong>„Gericht tauschen"</strong> auf der Kachel-Rückseite</li>
<li>Klick auf <strong>„Gericht wählen"</strong> CTA oder Vorschlag-Zeile auf einer leeren Kachel</li>
</ul>
<h3>Drawer-Verhalten</h3>
<ul>
<li>Slide-in von rechts, überlagert den Inhalt (kein Layout-Shift)</li>
<li>Breite: <code>min(480px, 90vw)</code></li>
<li>Backdrop (halbtransparent) schließt den Drawer bei Klick</li>
<li>Nach Auswahl: Drawer schließt sich, Slot wird aktualisiert, Kachel zeigt neues Rezept</li>
</ul>
<div class="callout">
Der bestehende <code>RecipePicker</code>-Komponente (aktuell im rechten Panel) wird in einen
generischen Drawer gewrappt. Der Drawer-Wrapper ist neu; der Picker selbst bleibt unverändert.
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-label">07 · Mobile</div>
<h2>Mobile — Out of Scope</h2>
<p>
Dieses Spec betrifft ausschließlich die Desktop-Ansicht (<code>≥ 768px</code>).
Das mobile Layout (vertikaler Stack, DayMealCard, ActionSheet) bleibt unverändert.
CSS-3D-Flips auf Touch-Geräten haben bekannte Rendering-Unterschiede auf älteren Android-Browsern —
ein separates Issue sollte die mobile Interaktion (ggf. Slide-up Sheet statt Flip) spezifizieren.
</p>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-label">08 · Komponenten</div>
<h2>Komponenten-Übersicht</h2>
<div class="component-row">
<span class="comp-file">src/routes/(app)/planner/+page.svelte</span>
<span class="badge badge-mod">Ändern</span>
<span class="comp-action">Rechtes Panel entfernen. Layout auf 2-spaltig (sidebar + main) umstellen. Toolbar-Button entfernen. Grid-Höhe auf 100% setzen.</span>
</div>
<div class="component-row">
<span class="comp-file">src/lib/planner/DayMealCard.svelte</span>
<span class="badge badge-mod">Ersetzen / umbenennen</span>
<span class="comp-action">Zur Flip-Kachel umbauen: .scene → .card → .card-front + .card-back. Farb-Klassen-Prop, Gradient-Overlay, Back-Face mit Aktionen.</span>
</div>
<div class="component-row">
<span class="comp-file">src/lib/planner/EmptyDayTile.svelte</span>
<span class="badge badge-new">Neu</span>
<span class="comp-action">Leere Kachel: + CTA + Inline-Suggestion-Liste mit Reasoning-Tags. Ersetzt den bisherigen leeren Slot-Platzhalter.</span>
</div>
<div class="component-row">
<span class="comp-file">src/lib/planner/RecipePickerDrawer.svelte</span>
<span class="badge badge-new">Neu</span>
<span class="comp-action">Drawer-Wrapper um den bestehenden RecipePicker. Slide-in von rechts, Backdrop, Schließ-Logik.</span>
</div>
<div class="component-row">
<span class="comp-file">src/lib/planner/RecipePicker.svelte</span>
<span class="badge badge-mod">Ändern</span>
<span class="comp-action">Aus dem rechten Panel lösen. Bekommt slotId als Prop. Keine Änderung an der Such-/Auswahl-Logik nötig.</span>
</div>
<div class="component-row">
<span class="comp-file">src/app.css</span>
<span class="badge badge-mod">Ergänzen</span>
<span class="comp-action">14 Farb-Klassen für Protein- und Küchenstil-Gradients hinzufügen (<code>.protein-haehnchen</code>, <code>.cuisine-asiatisch</code>, …).</span>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-label">09 · Accessibility</div>
<h2>A11y-Anforderungen</h2>
<ul>
<li><code>.scene</code>: <code>role="button"</code>, <code>tabindex="0"</code>, <code>aria-expanded="false|true"</code>, <code>aria-label="[Rezeptname] — Details anzeigen"</code></li>
<li><code>.card-back</code>: <code>aria-hidden="true"</code> solange nicht geflippt</li>
<li>× Schließen-Button: <code>aria-label="Schließen"</code>, <code>type="button"</code></li>
<li>Keyboard: <code>Enter</code> / <code>Space</code> flippt, <code>Escape</code> dreht zurück</li>
<li>Dimming: gedimmte Kacheln bekommen <code>aria-hidden="true"</code> wenn eine andere geflippt ist</li>
</ul>
</div>
</div>
</body>
</html>