Files
mealprep/specs/frontend/j2-add-meal.html
Marcel Raddatz 693ec2b997 feat(specs): Add-to-Plan flows — screens C4, C5, C6
Specifies three missing interaction surfaces for adding a recipe to
the weekly plan (Issue #42):

- C4: Recipe picker bottom sheet / desktop panel transformation
  triggered from the weekly planner's "+" button or empty slot
- C5: Recipe card quick actions ("Jetzt kochen" + "Zur Woche +")
- C6: Day picker sheet / panel for adding a known recipe to a chosen day

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

914 lines
73 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Recipe App — J2 Add to Plan · Screens C4C6</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"/>
<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;--green-deeper:#1E4A26;--yellow-tint:#FDF6D8;--yellow-light:#F9E08A;--yellow:#F2C12E;--yellow-dark:#C49610;--yellow-text:#8A6800;--blue-tint:#E6F1FB;--blue-light:#A4CFF4;--blue:#2D7DD2;--blue-dark:#185FA5;--purple-tint:#EEEDFE;--purple:#534AB7;--purple-dark:#3C3489;--orange-tint:#FEF0E6;--orange:#E8862A;--orange-dark:#B46820;--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-xl:16px;--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,.08),0 2px 4px rgba(28,28,24,.04);--shadow-overlay:0 8px 32px rgba(28,28,24,.12),0 2px 8px rgba(28,28,24,.06);}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
body{font-family:var(--font-sans);background:#E8E7E2;color:var(--color-text);font-size:14px;line-height:1.6;}
.doc{max-width:1200px;margin:0 auto;padding:48px 40px 120px;}
/* Header */
.doc-header{display:flex;justify-content:space-between;align-items:flex-end;background:var(--color-page);margin:-48px -40px 48px;padding:48px 40px 28px;border-bottom:1px solid var(--color-border);border-radius:var(--radius-xl) var(--radius-xl) 0 0;}
.doc-header h1{font-family:var(--font-display);font-size:28px;font-weight:500;letter-spacing:-.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;}
/* Sections */
.section{margin-bottom:64px;}
.section-title{font-size:10px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--color-text-muted);padding-bottom:10px;border-bottom:1px solid var(--color-border);margin-bottom:24px;}
/* Journey header */
.jh{padding:20px 24px;border-radius:var(--radius-xl);margin-bottom:40px;display:flex;align-items:center;gap:16px;}
.jh .jn{font-family:var(--font-display);font-size:48px;font-weight:300;line-height:1;opacity:.5;}
.jh h2{font-family:var(--font-display);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
.jh p{font-size:13px;line-height:1.5;}.jh .fl{font-family:var(--font-mono);font-size:11px;margin-top:6px;opacity:.7;}
.jh-y{background:var(--yellow-tint);border:1px solid var(--yellow-light);}.jh-y .jn{color:var(--yellow-dark);}.jh-y p,.jh-y .fl{color:var(--yellow-text);}
/* Screen block */
.scr{margin-bottom:56px;}
.scr-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;}
.scr-head h3{font-family:var(--font-display);font-size:20px;font-weight:500;letter-spacing:-.02em;}
.scr-id{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);padding:2px 8px;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-page);}
.scr-desc{font-size:12px;color:var(--color-text-muted);line-height:1.6;max-width:720px;margin-bottom:6px;}
.scr-var{font-size:11px;color:var(--color-text-muted);margin-bottom:20px;}.scr-var strong{color:var(--color-text);}
/* Preview container */
.previews{display:flex;gap:32px;flex-wrap:wrap;justify-content:center;align-items:flex-start;margin-bottom:20px;}
.prev-col{display:flex;flex-direction:column;align-items:center;gap:10px;}
.bp-lbl{font-family:var(--font-mono);font-size:10px;color:var(--color-text-muted);}
/* Phone frame */
.phone{width:320px;flex-shrink:0;background:var(--color-page);border-radius:36px;overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.07);display:flex;flex-direction:column;border:6px solid #1C1C18;}
.pst{padding:10px 20px 0;display:flex;justify-content:space-between;align-items:center;font-size:12px;background:var(--color-page);}.pst b{font-weight:600;}.pst span{font-size:10px;}
.pb{flex:1;display:flex;flex-direction:column;}
/* Desktop frame */
.desk{width:100%;max-width:1040px;background:var(--color-page);border-radius:var(--radius-xl);overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.06);display:flex;min-height:520px;}
/* Desktop sidebar */
.dsb{width:196px;flex-shrink:0;background:var(--color-surface);border-right:1px solid var(--color-border);display:flex;flex-direction:column;}
.dsb-logo{padding:16px 14px 12px;border-bottom:1px solid var(--color-border);}
.dsb-lm{display:flex;align-items:center;gap:6px;margin-bottom:2px;}.dsb-ic{width:22px;height:22px;border-radius:4px;background:var(--green);display:flex;align-items:center;justify-content:center;font-size:11px;}.dsb-nm{font-family:var(--font-display);font-size:15px;font-weight:500;letter-spacing:-.02em;}.dsb-sub{font-size:10px;color:var(--color-text-muted);padding-left:28px;}
.dsb-nav{padding:10px 8px;flex:1;}
.dsb-ni{display:flex;align-items:center;gap:6px;padding:6px 6px;border-radius:5px;font-size:12px;color:var(--color-text-muted);margin-bottom:1px;}
.dsb-ni.a{background:var(--green-tint);color:var(--green-dark);font-weight:500;}
.dsb-nc{font-size:12px;width:16px;text-align:center;}
.dsb-var{margin:0 8px 12px;padding:10px 12px;background:var(--yellow-tint);border:1px solid var(--yellow-light);border-radius:var(--radius-lg);}
.dsb-ve{font-size:8px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--yellow-text);margin-bottom:3px;}
.dsb-vn{font-family:var(--font-display);font-size:28px;font-weight:300;line-height:1;color:var(--color-text);}
/* Desktop main */
.d-main{flex:1;display:flex;flex-direction:column;min-width:0;}
.d-tb{padding:10px 16px;border-bottom:1px solid var(--color-border);display:flex;justify-content:space-between;align-items:center;flex-shrink:0;}
.d-ttitle{font-family:var(--font-display);font-size:16px;font-weight:500;letter-spacing:-.02em;}
.d-ab{font-family:var(--font-sans);font-size:12px;font-weight:500;padding:5px 12px;border-radius:4px;background:var(--green);color:#fff;border:none;}
.d-content{flex:1;display:flex;overflow:hidden;}
.d-cal{flex:1;overflow-y:auto;padding:10px;}
.d-cg{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;}
.d-cc{display:flex;flex-direction:column;}
.d-ch{text-align:center;padding-bottom:6px;margin-bottom:6px;border-bottom:2px solid var(--color-border);}
.d-ch.th{border-bottom-color:var(--yellow);}
.d-ch.sh{border-bottom-color:var(--green);}
.d-dn{font-size:8px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:3px;}
.d-db{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:4px;font-size:11px;font-weight:500;color:var(--color-text);}
.d-ch.th .d-db{background:var(--yellow);}
.d-ch.sh .d-db{background:var(--green-tint);color:var(--green-dark);}
.d-tile{flex:1;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:6px 6px 8px;cursor:pointer;box-shadow:var(--shadow-card);display:flex;flex-direction:column;gap:2px;}
.d-tile.tt{border-color:var(--yellow);border-width:2px;background:var(--yellow-tint);}
.d-te{font-size:7px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted);}
.d-tn{font-family:var(--font-display);font-size:10px;font-weight:400;letter-spacing:-.01em;color:var(--color-text);line-height:1.3;}
.d-tm{font-size:8px;color:var(--color-text-muted);margin-top:1px;}
.d-et{flex:1;border:1px dashed var(--color-border);border-radius:var(--radius-lg);display:flex;align-items:center;justify-content:center;min-height:56px;flex-direction:column;gap:2px;cursor:pointer;}
.d-et.active{border-style:solid;border-color:var(--green);background:var(--green-tint);}
.d-ep{font-size:15px;color:var(--color-border);}
.d-el{font-size:8px;color:var(--color-border);}
.d-et.active .d-ep,.d-et.active .d-el{color:var(--green-dark);}
/* Detail panel */
.d-dp{width:216px;flex-shrink:0;background:var(--color-surface);border-left:1px solid var(--color-border);display:flex;flex-direction:column;overflow-y:auto;}
.d-dph{padding:10px 12px 8px;border-bottom:1px solid var(--color-border);display:flex;justify-content:space-between;align-items:flex-start;}
.d-dpd{font-family:var(--font-display);font-size:14px;font-weight:500;letter-spacing:-.01em;}
.d-dpdt{font-size:9px;color:var(--color-text-muted);margin-top:1px;}
.d-dpx{font-size:16px;color:var(--color-text-muted);cursor:pointer;line-height:1;flex-shrink:0;}
.d-psearch{padding:8px 10px;border-bottom:1px solid var(--color-subtle);}
.d-psi{width:100%;font-family:var(--font-sans);font-size:11px;padding:6px 8px;border-radius:var(--radius-md);border:1px solid var(--color-border);background:var(--color-page);color:var(--color-text);outline:none;}
.d-prsect{font-size:7px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);padding:6px 10px 3px;background:var(--color-subtle);}
.d-prec{padding:7px 10px;border-bottom:1px solid var(--color-subtle);cursor:pointer;}
.d-prec:last-child{border-bottom:none;}
.d-prname{font-family:var(--font-display);font-size:11px;font-weight:400;color:var(--color-text);line-height:1.25;}
.d-prmeta{font-size:9px;color:var(--color-text-muted);margin-top:1px;}
.d-prbadge{display:inline-block;font-size:8px;font-weight:500;padding:1px 5px;border-radius:3px;background:var(--green-tint);color:var(--green-dark);margin-top:2px;}
.d-prbadge.warn{background:var(--yellow-tint);color:var(--yellow-text);}
/* Bottom tab nav */
.mbt{border-top:1px solid var(--color-border);background:var(--color-surface);padding:6px 16px 20px;display:flex;justify-content:space-around;}
.mt-i{display:flex;flex-direction:column;align-items:center;gap:2px;}.mt-ic{width:18px;height:18px;border-radius:4px;background:var(--color-subtle);display:flex;align-items:center;justify-content:center;font-size:10px;}.mt-i.a .mt-ic{background:var(--green-tint);}.mt-l{font-size:8px;font-weight:500;color:var(--color-text-muted);}.mt-i.a .mt-l{color:var(--green-dark);}
/* Shared badges */
.badge{font-size:9px;font-weight:500;padding:2px 6px;border-radius:3px;display:inline-block;}
.badge-g{background:var(--green-tint);color:var(--green-dark);}
.badge-y{background:var(--yellow-tint);color:var(--yellow-text);}
.badge-m{background:var(--color-subtle);color:var(--color-text-muted);}
/* Eyebrow */
.eye{font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);}
/* Annotation cards */
.ann{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:20px 24px;margin-bottom:12px;}
.ann h3{font-size:13px;font-weight:500;color:var(--color-text);margin-bottom:8px;}
.ann p{font-size:13px;color:var(--color-text-muted);line-height:1.65;}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;}
.grid3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-top:16px;}
.note{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px 20px;}
.note h4{font-size:11px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:8px;}
.note p{font-size:13px;color:var(--color-text-muted);line-height:1.6;}
.note.hl{border-color:var(--green-light);background:var(--green-tint);}
.note.hl h4{color:var(--green-dark);}
.note.hl p{color:var(--green-deeper);}
.note.warn{border-color:var(--yellow-light);background:var(--yellow-tint);}
.note.warn h4{color:var(--yellow-text);}
.note.warn p{color:var(--yellow-text);}
/* Agent section */
.agent{background:var(--color-text);color:#E8E8E2;padding:24px;border-radius:var(--radius-lg);margin-top:20px;}
.agent h4{font-size:9px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:#5A5A55;margin-bottom:12px;}
.agent pre{font-family:var(--font-mono);font-size:10px;color:#444440;margin-bottom:16px;line-height:1.8;white-space:pre-wrap;}
.at{width:100%;border-collapse:collapse;font-family:var(--font-mono);font-size:10px;}
.at thead tr{border-bottom:1px solid #2A2A26;}.at th{text-align:left;padding:6px 10px;font-size:8px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:#5A5A55;font-family:var(--font-sans);}.at td{padding:5px 10px;border-bottom:1px solid #1E1E1A;vertical-align:top;line-height:1.5;}.at tr:last-child td{border-bottom:none;}.at td:first-child{color:#7A7A72;}.at td:nth-child(2){color:#E8E8E2;font-weight:500;}.at td:nth-child(3){color:#5A5A55;}.at .grp td{padding-top:14px;font-family:var(--font-sans);font-size:8px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:#3A3A36;}
/* ── C4: Recipe picker bottom sheet ── */
.sheet-host{flex:1;display:flex;flex-direction:column;justify-content:flex-end;min-height:400px;}
.sheet-bg{position:relative;flex:1;background:rgba(28,28,24,.4);}
.sheet{background:var(--color-page);border-top:1px solid var(--color-border);border-radius:var(--radius-xl) var(--radius-xl) 0 0;box-shadow:0 -4px 20px rgba(0,0,0,.12);display:flex;flex-direction:column;max-height:320px;}
.sheet-handle{width:32px;height:4px;border-radius:2px;background:var(--color-border);margin:10px auto 0;flex-shrink:0;}
.sheet-hd{padding:8px 14px 8px;border-bottom:1px solid var(--color-subtle);display:flex;justify-content:space-between;align-items:center;flex-shrink:0;}
.sheet-ht{font-family:var(--font-display);font-size:14px;font-weight:500;letter-spacing:-.01em;}
.sheet-sub{font-size:10px;color:var(--color-text-muted);margin-top:1px;}
.sheet-x{font-size:17px;color:var(--color-text-muted);cursor:pointer;line-height:1;}
.sheet-search{padding:7px 12px;border-bottom:1px solid var(--color-subtle);flex-shrink:0;}
.sheet-si{width:100%;font-family:var(--font-sans);font-size:11px;padding:6px 8px 6px 26px;border-radius:var(--radius-md);border:1px solid var(--color-border);background:var(--color-surface);color:var(--color-text);outline:none;}
.sheet-si-wrap{position:relative;}
.sheet-si-icon{position:absolute;left:8px;top:50%;transform:translateY(-50%);font-size:10px;color:var(--color-text-muted);}
.sheet-body{overflow-y:auto;flex:1;}
.sheet-sect{font-size:7px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);padding:5px 12px 3px;background:var(--color-subtle);}
.sheet-row{padding:7px 12px;border-bottom:1px solid var(--color-subtle);display:flex;align-items:center;gap:8px;}
.sheet-row:last-child{border-bottom:none;}
.sheet-rinfo{flex:1;}
.sheet-rn{font-family:var(--font-display);font-size:12px;font-weight:400;color:var(--color-text);line-height:1.25;}
.sheet-rm{font-size:9px;color:var(--color-text-muted);margin-top:1px;}
.sheet-rbadge{display:inline-block;font-size:8px;font-weight:500;padding:1px 5px;border-radius:3px;background:var(--green-tint);color:var(--green-dark);margin-top:2px;}
.sheet-rbadge.warn{background:var(--yellow-tint);color:var(--yellow-text);}
.sheet-add{font-family:var(--font-sans);font-size:10px;font-weight:500;padding:4px 8px;border-radius:var(--radius-md);background:var(--green);color:#fff;border:none;white-space:nowrap;flex-shrink:0;}
/* ── C5: Recipe quick actions ── */
.rc-list{padding:8px 12px;overflow-y:auto;}
.rc-card{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:10px 12px;margin-bottom:8px;box-shadow:var(--shadow-card);}
.rc-name{font-family:var(--font-display);font-size:14px;font-weight:400;letter-spacing:-.01em;color:var(--color-text);line-height:1.25;margin-bottom:4px;}
.rc-tags{display:flex;flex-wrap:wrap;gap:3px;margin-bottom:8px;}
.rc-acts{display:flex;gap:5px;}
.rc-cook{flex:1;font-family:var(--font-sans);font-size:10px;font-weight:500;padding:5px 6px;border-radius:var(--radius-md);background:var(--green);color:#fff;border:none;text-align:center;}
.rc-plan{flex:1;font-family:var(--font-sans);font-size:10px;font-weight:500;padding:5px 6px;border-radius:var(--radius-md);background:var(--green-tint);color:var(--green-dark);border:1px solid var(--green-light);text-align:center;}
/* Desktop recipe grid */
.d-rcgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;padding:16px;}
.d-rccard{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;box-shadow:var(--shadow-card);}
.d-rcname{font-family:var(--font-display);font-size:16px;font-weight:400;letter-spacing:-.01em;color:var(--color-text);margin-bottom:5px;}
.d-rcdesc{font-size:12px;color:var(--color-text-muted);line-height:1.5;margin-bottom:10px;}
.d-rctags{display:flex;flex-wrap:wrap;gap:4px;margin-bottom:12px;}
.d-rcacts{display:flex;gap:6px;}
.d-rccook{flex:1;font-family:var(--font-sans);font-size:11px;font-weight:500;padding:7px;border-radius:var(--radius-md);background:var(--green);color:#fff;border:none;text-align:center;}
.d-rcplan{flex:1;font-family:var(--font-sans);font-size:11px;font-weight:500;padding:7px;border-radius:var(--radius-md);background:var(--green-tint);color:var(--green-dark);border:1px solid var(--green-light);text-align:center;}
/* ── C6: Day picker ── */
.dp-strip{display:grid;grid-template-columns:repeat(7,1fr);gap:3px;padding:8px 12px 5px;}
.dp-chip{display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 2px;border-radius:5px;border:1px solid transparent;cursor:pointer;}
.dp-chip.empty{border-style:dashed;border-color:var(--green-light);background:var(--green-tint);}
.dp-chip.filled{border-color:var(--color-border);background:var(--color-surface);}
.dp-chip.today{border-color:var(--yellow);background:var(--yellow-tint);}
.dp-chip.sel-empty{border:2px solid var(--green-dark);background:var(--green-tint);}
.dp-chip.sel-filled{border:2px solid var(--orange-dark);background:var(--orange-tint);}
.dp-ca{font-size:7px;font-weight:500;letter-spacing:.05em;text-transform:uppercase;color:var(--color-text-muted);}
.dp-chip.empty .dp-ca{color:var(--green-dark);}
.dp-chip.today .dp-ca{color:var(--yellow-text);}
.dp-chip.sel-empty .dp-ca{color:var(--green-deeper);}
.dp-chip.sel-filled .dp-ca{color:var(--orange-dark);}
.dp-cn{font-size:11px;font-weight:500;color:var(--color-text);}
.dp-dot{width:4px;height:4px;border-radius:50%;background:var(--color-border);margin-top:1px;}
.dp-chip.empty .dp-dot{background:transparent;}
.dp-chip.filled .dp-dot,.dp-chip.sel-empty .dp-dot{background:var(--green);}
.dp-chip.today .dp-dot{background:var(--yellow-text);}
.dp-chip.sel-filled .dp-dot{background:var(--orange-dark);}
.dp-confirm{padding:8px 12px 6px;}
.dp-btn{width:100%;font-family:var(--font-sans);font-size:12px;font-weight:500;padding:9px;border-radius:var(--radius-md);background:var(--green);color:#fff;border:none;text-align:center;}
.dp-btn.replace{background:var(--orange);}
.dp-warn{background:var(--orange-tint);border:1px solid #FBCDA4;border-radius:var(--radius-md);padding:6px 10px;margin:0 12px 6px;font-size:10px;color:var(--orange-dark);line-height:1.45;}
/* LLM section */
.llm{background:var(--color-text);color:#E8E8E2;padding:36px 44px;border-radius:var(--radius-lg);margin-top:80px;}
.llm h2{font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:#6B6A63;margin-bottom:16px;}
.llm h3{font-size:12px;font-weight:500;color:#9A9990;margin:24px 0 8px;letter-spacing:.04em;text-transform:uppercase;}
.llm p,.llm li{font-size:12px;color:#9A9990;line-height:1.7;}
.llm ul{margin-left:16px;margin-bottom:8px;}
.llm strong{color:#E8E8E2;}
.llm code{font-family:var(--font-mono);font-size:10px;background:#1E1E1A;color:#A0A090;padding:1px 5px;border-radius:3px;}
@media(max-width:900px){.doc{padding:24px 16px 80px;}}
</style>
</head>
<body>
<div class="doc">
<div class="doc-header">
<div>
<h1>J2 Add to Plan — Screens C4C6</h1>
<p>Supplemental spec · Bottom sheet recipe picker · Recipe quick actions · Day picker</p>
</div>
<div class="doc-meta">
Journey: J2 supplement<br>
Screens: C4 · C5 · C6<br>
Status: draft<br>
Version: 1.0<br>
Updated: 2026-04
</div>
</div>
<div class="jh jh-y">
<div class="jn">J2</div>
<div>
<h2>Add to plan — supplemental</h2>
<p>Three missing flows: picking a recipe for an empty slot (C4), quick actions on recipe cards (C5), and the day picker when the recipe is already known (C6).</p>
<div class="fl">Two entry points: C1 "+" / empty slot → C4 · B1 "Zur Woche +" → C6</div>
</div>
</div>
<!-- RATIONALE -->
<div class="section">
<div class="section-title">Design rationale</div>
<div class="grid2">
<div class="ann">
<h3>Entry from planner (C4) — recipe unknown</h3>
<p>User taps "+" or an empty slot in C1. The day is known; the recipe isn't. A bottom sheet slides up over the dimmed planner, showing variety-ranked suggestions and a search-filtered full library. On desktop the empty tile highlights and the detail panel transforms to a recipe picker — the calendar grid stays fully visible. No full-page navigation; same pattern as J4 swap.</p>
</div>
<div class="ann">
<h3>Entry from recipe list (C5 → C6) — day unknown</h3>
<p>User is browsing recipes and taps "Zur Woche +". The recipe is known; the day isn't. A compact day-picker sheet shows the week's slots. Empty slots have a dashed green border to invite selection. Selecting a filled slot shows an inline replace warning — no modal dialog. "Jetzt kochen" on the same card navigates directly to J3 cook mode.</p>
</div>
</div>
<div class="grid3">
<div class="note warn">
<h4>Add ≠ Swap</h4>
<p>J4 Swap starts from a filled slot and shows system-suggested replacements. C4/C6 add starts from an empty slot or a chosen recipe. Different intent — do not reuse the swap sheet component.</p>
</div>
<div class="note hl">
<h4>Mobile: always bottom sheet</h4>
<p>No full-page navigation for either entry point. The planner or recipe list context stays visible behind the sheet at 40% opacity.</p>
</div>
<div class="note">
<h4>Desktop: panel transformation</h4>
<p>The 216px detail panel switches between states. No modal overlay. The calendar grid or recipe grid remains fully interactive beside the picker.</p>
</div>
</div>
</div>
<!-- ═══ C4: ADD MEAL FROM PLANNER ═══ -->
<div class="scr" id="c4">
<div class="scr-head"><h3>Add meal — from planner</h3><span class="scr-id">C4</span></div>
<div class="scr-desc">Entry: tap "+" in C1 nav (pre-selects next empty day) or tap any empty slot chip/tile (pre-selects that day). Shows a bottom sheet with search + variety-ranked suggestions + full recipe list. Tapping a recipe immediately fills the slot — no confirmation, undo toast instead.</div>
<div class="scr-var"><strong>V1 · Bottom sheet (mobile &amp; tablet)</strong> · <strong>V2 · Panel transformation (desktop)</strong></div>
<div class="section">
<div class="section-title">Breakpoint 1 — Mobile · &lt; 768px</div>
<div style="display:flex;gap:40px;align-items:flex-start;flex-wrap:wrap;margin-bottom:32px;">
<div class="phone">
<div class="pst"><b>9:41</b><span>●●● WiFi 🔋</span></div>
<div class="pb">
<!-- Planner context, dimmed -->
<div style="opacity:.3;">
<div style="padding:8px 12px;border-bottom:1px solid var(--color-border);display:flex;justify-content:space-between;align-items:center;">
<div style="font-family:var(--font-display);font-size:16px;font-weight:500;">Diese Woche</div>
<div style="display:flex;gap:3px;">
<div style="width:22px;height:22px;border-radius:4px;border:1px solid var(--color-border);background:var(--color-surface);display:flex;align-items:center;justify-content:center;font-size:9px;color:var(--color-text-muted);"></div>
<div style="width:22px;height:22px;border-radius:4px;border:1px solid var(--color-border);background:var(--color-surface);display:flex;align-items:center;justify-content:center;font-size:9px;color:var(--color-text-muted);"></div>
<div style="width:22px;height:22px;border-radius:4px;background:var(--green);display:flex;align-items:center;justify-content:center;font-size:10px;color:#fff;">+</div>
</div>
</div>
<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:2px;padding:5px 10px;">
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 1px;border-radius:4px;"><span style="font-size:7px;font-weight:500;text-transform:uppercase;color:var(--color-text-muted);">Mo</span><span style="font-size:10px;font-weight:500;">31</span><div style="width:3px;height:3px;border-radius:50%;background:var(--green);"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 1px;border-radius:4px;background:var(--yellow-tint);"><span style="font-size:7px;font-weight:500;text-transform:uppercase;color:var(--yellow-text);">Di</span><span style="font-size:10px;font-weight:500;">1</span><div style="width:3px;height:3px;border-radius:50%;background:var(--yellow-text);"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 1px;border-radius:4px;"><span style="font-size:7px;font-weight:500;text-transform:uppercase;color:var(--color-text-muted);">Mi</span><span style="font-size:10px;font-weight:500;">2</span><div style="width:3px;height:3px;border-radius:50%;background:var(--green);"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 1px;border-radius:4px;"><span style="font-size:7px;font-weight:500;text-transform:uppercase;color:var(--color-text-muted);">Do</span><span style="font-size:10px;font-weight:500;">3</span><div style="width:3px;height:3px;border-radius:50%;background:var(--green);"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 1px;border-radius:4px;"><span style="font-size:7px;font-weight:500;text-transform:uppercase;color:var(--color-text-muted);">Fr</span><span style="font-size:10px;font-weight:500;">4</span><div style="width:3px;height:3px;border-radius:50%;background:var(--green);"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 1px;border-radius:4px;border:1px dashed var(--green-light);background:var(--green-tint);"><span style="font-size:7px;font-weight:500;text-transform:uppercase;color:var(--green-dark);">Sa</span><span style="font-size:10px;font-weight:500;">5</span><div style="width:3px;height:3px;border-radius:50%;"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 1px;border-radius:4px;"><span style="font-size:7px;font-weight:500;text-transform:uppercase;color:var(--color-text-muted);">So</span><span style="font-size:10px;font-weight:500;">6</span><div style="width:3px;height:3px;border-radius:50%;background:var(--green);"></div></div>
</div>
</div>
<!-- Sheet -->
<div class="sheet-host">
<div class="sheet-bg"></div>
<div class="sheet">
<div class="sheet-handle"></div>
<div class="sheet-hd">
<div>
<div class="sheet-ht">Rezept wählen</div>
<div class="sheet-sub">Samstag, 5. April</div>
</div>
<div class="sheet-x">×</div>
</div>
<div class="sheet-search">
<div class="sheet-si-wrap">
<span class="sheet-si-icon">🔍</span>
<input class="sheet-si" type="text" placeholder="Rezept suchen…" readonly/>
</div>
</div>
<div class="sheet-body">
<div class="sheet-sect">Empfohlen · Beste Abwechslung</div>
<div class="sheet-row">
<div class="sheet-rinfo">
<div class="sheet-rn">Lachsfilet mit Gemüse</div>
<div class="sheet-rm">25 min · Einfach · Fisch</div>
<span class="sheet-rbadge">↑ +2 Punkte</span>
</div>
<button class="sheet-add">+ Wählen</button>
</div>
<div class="sheet-row">
<div class="sheet-rinfo">
<div class="sheet-rn">Mushroom Risotto</div>
<div class="sheet-rm">50 min · Mittel · Vegetarisch</div>
<span class="sheet-rbadge">↑ +2 Punkte</span>
</div>
<button class="sheet-add">+ Wählen</button>
</div>
<div class="sheet-row">
<div class="sheet-rinfo">
<div class="sheet-rn">Hähnchen-Curry</div>
<div class="sheet-rm">35 min · Einfach</div>
<span class="sheet-rbadge warn">⚠ Fr ebenfalls Hähnchen</span>
</div>
<button class="sheet-add">+ Wählen</button>
</div>
<div class="sheet-sect">Alle Rezepte</div>
<div class="sheet-row">
<div class="sheet-rinfo"><div class="sheet-rn">Beef Bourguignon</div><div class="sheet-rm">2h 30 · Aufwendig</div></div>
<button class="sheet-add">+ Wählen</button>
</div>
<div class="sheet-row">
<div class="sheet-rinfo"><div class="sheet-rn">Spaghetti Carbonara</div><div class="sheet-rm">20 min · Einfach</div></div>
<button class="sheet-add">+ Wählen</button>
</div>
<div class="sheet-row">
<div class="sheet-rinfo"><div class="sheet-rn">Tomatensuppe</div><div class="sheet-rm">30 min · Vegetarisch</div></div>
<button class="sheet-add">+ Wählen</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="flex:1;min-width:240px;display:flex;flex-direction:column;gap:12px;">
<div class="note hl">
<h4>Sheet structure</h4>
<p>Drag handle → title row (day date in muted subtitle) → close × → search input → two sections: "Empfohlen" (24 variety-ranked, sorted by variety_delta DESC) + "Alle Rezepte" (full library, filtered by search). Max height ~75vh — 3 suggestions visible without scrolling.</p>
</div>
<div class="note">
<h4>Variety badges</h4>
<p>Green "↑ +N Punkte" when adding this recipe improves the variety score. Yellow "⚠ [reason]" when it creates a conflict but is still selectable. No badge in the "Alle Rezepte" section.</p>
</div>
<div class="note">
<h4>Background</h4>
<p>Weekly planner dims to 30% opacity behind the sheet. The week strip is still legible — the empty Sa chip is visible with its dashed border, providing context for which slot is being filled.</p>
</div>
<div class="note warn">
<h4>Immediate write</h4>
<p>Tapping "+ Wählen" writes to the slot immediately and dismisses the sheet. An undo toast appears for 4 seconds. No confirmation dialog — same pattern as J4.</p>
</div>
</div>
</div>
</div>
<div class="section">
<div class="section-title">Breakpoint 3 — Desktop · &gt; 1024px</div>
<div class="ann" style="margin-bottom:16px;">
<h3>Panel in recipe-picker mode</h3>
<p>Clicking an empty slot tile (or "+" in the toolbar without a day) opens the recipe picker in the right detail panel. The calendar grid remains fully visible and interactive. The empty tile highlights with a solid green border and "Wählen…" label to indicate which slot is being edited.</p>
</div>
<div style="overflow-x:auto;margin-bottom:20px;">
<div class="desk" style="min-height:460px;">
<div class="dsb">
<div class="dsb-logo"><div class="dsb-lm"><div class="dsb-ic">🥗</div><div class="dsb-nm">Mealplan</div></div><div class="dsb-sub">Smith household</div></div>
<div class="dsb-nav">
<div class="dsb-ni a"><span class="dsb-nc">📅</span> Planer</div>
<div class="dsb-ni"><span class="dsb-nc">📖</span> Rezepte</div>
<div class="dsb-ni"><span class="dsb-nc">🛒</span> Einkauf</div>
</div>
<div class="dsb-var"><div class="dsb-ve">Variety score</div><div class="dsb-vn">8<span style="font-size:11px;color:var(--color-text-muted);">/10</span></div></div>
</div>
<div class="d-main">
<div class="d-tb">
<div style="display:flex;align-items:center;gap:12px;">
<span class="d-ttitle">Wochenplanung</span>
<div style="display:flex;gap:4px;align-items:center;">
<button style="font-family:var(--font-sans);font-size:10px;padding:3px 7px;border-radius:4px;background:var(--color-subtle);border:1px solid var(--color-border);color:var(--color-text-muted);"></button>
<span style="font-size:11px;font-weight:500;color:var(--color-text);min-width:120px;text-align:center;">31 März 6 Apr</span>
<button style="font-family:var(--font-sans);font-size:10px;padding:3px 7px;border-radius:4px;background:var(--color-subtle);border:1px solid var(--color-border);color:var(--color-text-muted);"></button>
</div>
</div>
<button class="d-ab">+ Mahlzeit</button>
</div>
<div class="d-content">
<div class="d-cal">
<div class="d-cg">
<div class="d-cc"><div class="d-ch"><div class="d-dn">Montag</div><div class="d-db">31</div></div><div class="d-tile"><div class="d-te">Abendessen</div><div class="d-tn">Pasta al forno</div><div class="d-tm">30 min</div></div></div>
<div class="d-cc"><div class="d-ch th"><div class="d-dn">Dienstag</div><div class="d-db">1</div></div><div class="d-tile tt"><div class="d-te">Heute</div><div class="d-tn">Gegrillter Lachs</div><div class="d-tm">25 min</div></div></div>
<div class="d-cc"><div class="d-ch"><div class="d-dn">Mittwoch</div><div class="d-db">2</div></div><div class="d-tile"><div class="d-te">Abendessen</div><div class="d-tn">Tomaten-Pasta</div><div class="d-tm">45 min</div></div></div>
<div class="d-cc"><div class="d-ch"><div class="d-dn">Donnerstag</div><div class="d-db">3</div></div><div class="d-tile"><div class="d-te">Abendessen</div><div class="d-tn">Hähnchen-Stir-fry</div><div class="d-tm">25 min</div></div></div>
<div class="d-cc"><div class="d-ch"><div class="d-dn">Freitag</div><div class="d-db">4</div></div><div class="d-tile"><div class="d-te">Abendessen</div><div class="d-tn">Hähnchen-Curry</div><div class="d-tm">40 min</div></div></div>
<div class="d-cc"><div class="d-ch sh"><div class="d-dn">Samstag</div><div class="d-db">5</div></div><div class="d-et active"><div class="d-ep">+</div><div class="d-el">Wählen…</div></div></div>
<div class="d-cc"><div class="d-ch"><div class="d-dn">Sonntag</div><div class="d-db">6</div></div><div class="d-tile"><div class="d-te">Abendessen</div><div class="d-tn">Linsensuppe</div><div class="d-tm">45 min</div></div></div>
</div>
</div>
<!-- Detail panel: recipe picker mode -->
<div class="d-dp">
<div class="d-dph">
<div><div class="d-dpd">Sa, 5. April</div><div class="d-dpdt">Rezept wählen</div></div>
<div class="d-dpx">×</div>
</div>
<div class="d-psearch">
<input class="d-psi" type="text" placeholder="🔍 Rezept suchen…" readonly/>
</div>
<div class="d-prsect">Empfohlen</div>
<div class="d-prec">
<div class="d-prname">Lachsfilet mit Gemüse</div>
<div class="d-prmeta">25 min · Einfach · Fisch</div>
<span class="d-prbadge">↑ +2 Punkte</span>
</div>
<div class="d-prec">
<div class="d-prname">Mushroom Risotto</div>
<div class="d-prmeta">50 min · Mittel · Vegetarisch</div>
<span class="d-prbadge">↑ +2 Punkte</span>
</div>
<div class="d-prec">
<div class="d-prname">Hähnchen-Curry</div>
<div class="d-prmeta">35 min · Einfach</div>
<span class="d-prbadge warn">⚠ Fr ebenfalls Hähnchen</span>
</div>
<div class="d-prsect">Alle Rezepte</div>
<div class="d-prec"><div class="d-prname">Beef Bourguignon</div><div class="d-prmeta">2h 30 · Aufwendig</div></div>
<div class="d-prec"><div class="d-prname">Spaghetti Carbonara</div><div class="d-prmeta">20 min · Einfach</div></div>
<div class="d-prec"><div class="d-prname">Tomatensuppe</div><div class="d-prmeta">30 min · Vegetarisch</div></div>
<div class="d-prec"><div class="d-prname">Rindereintopf</div><div class="d-prmeta">1h 20 · Mittel</div></div>
</div>
</div>
</div>
</div>
</div>
<div class="grid3">
<div class="note hl"><h4>Active empty tile</h4><p>Clicked empty slot: solid green border (replaces dashed), green-tint bg, green-dark "+" icon and "Wählen…" label. Provides clear visual anchor for which slot the panel is operating on.</p></div>
<div class="note"><h4>Panel header state</h4><p>"Sa, 5. April" in Fraunces 14px / "Rezept wählen" in 9px muted below. Close × returns panel to idle state. If a filled day was previously selected, × returns to that day's detail view.</p></div>
<div class="note"><h4>Clicking a recipe row</h4><p>Immediate PATCH to slot. Panel switches to the day-detail view for the newly filled slot. No undo toast needed on desktop — the panel immediately shows the result with a "Swap" option if the user wants to change it.</p></div>
</div>
</div>
<div class="agent">
<h4>C4 · Add meal from planner — implementation</h4>
<pre>/* TWO ENTRY POINTS:
* 1. "+" button in C1 nav → pre-selects next empty day in week (MonSun scan).
* 2. Empty day slot chip (mobile) / empty tile (desktop) → pre-selects that date.
*
* MOBILE: bottom sheet slides up, planner dims to 30% opacity behind.
* Sheet: drag handle + {day label} header + × + search input + two sections.
* "Empfohlen" section: GET /api/suggestions?week={id}&day={date}
* → sorted by variety_delta DESC (unlike J4 which sorts by effort ASC).
* → 24 items. Badge: green "↑ +N Punkte" if delta > 0, yellow "⚠ {reason}" if ≤ 0.
* "Alle Rezepte": GET /api/recipes?sort=name — no badge.
* Search input: client-side filter of visible list. Does not re-sort.
* "+ Wählen" tap: PATCH /api/week-plan/{weekId}/slots/{date} {recipe_id}
* → dismiss sheet → undo toast 4s with "Rückgängig" link.
*
* DESKTOP: tap empty tile → detail panel enters recipe-picker state.
* Empty tile visual: solid green border, green-tint bg, "Wählen…" label.
* Panel state machine: idle | day-detail | recipe-picker | day-picker
* Clicking panel recipe row: PATCH → panel transitions to day-detail for that date.
* No undo toast on desktop (panel shows result immediately with Swap option). */</pre>
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
<tr class="grp"><td colspan="3">Mobile sheet</td></tr>
<tr><td>Sheet max-height</td><td>75vh</td><td>Drag to dismiss</td></tr>
<tr><td>Dim opacity</td><td>rgba(28,28,24,.4)</td><td>Same as J4</td></tr>
<tr><td>Suggestion sort</td><td>variety_delta DESC</td><td>Different from J4 (effort ASC)</td></tr>
<tr><td>Suggestion badge</td><td>8px, green-tint or yellow-tint</td><td>Only in Empfohlen section</td></tr>
<tr><td>Write action</td><td>PATCH + undo toast 4s</td><td>No confirmation dialog</td></tr>
<tr class="grp"><td colspan="3">Desktop panel</td></tr>
<tr><td>Active empty tile</td><td>solid green border, green-tint bg</td><td>Replaces dashed border</td></tr>
<tr><td>Panel width</td><td>216px (unchanged)</td><td>Same panel, different content state</td></tr>
<tr><td>Panel recipe click</td><td>PATCH → transition to day-detail</td><td>No toast needed</td></tr>
</tbody></table>
</div>
</div>
<!-- ═══ C5: RECIPE QUICK ACTIONS ═══ -->
<div class="scr" id="c5">
<div class="scr-head"><h3>Recipe card — quick actions</h3><span class="scr-id">C5</span></div>
<div class="scr-desc">Recipe list cards (Recipes tab, B1) gain two quick action buttons below the tags row. "Jetzt kochen" navigates directly to J3 cook mode. "Zur Woche +" triggers the C6 day picker. Both buttons are always visible — no hover-only pattern, since touch-capable desktops don't have reliable hover.</div>
<div class="scr-var"><strong>V1 · Always-visible quick action row</strong> — mobile list and desktop grid</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Mobile · Recipe list</div>
<div class="phone">
<div class="pst"><b>9:41</b><span>●●● WiFi 🔋</span></div>
<div class="pb">
<div style="padding:8px 14px;border-bottom:1px solid var(--color-border);display:flex;justify-content:space-between;align-items:center;">
<div style="font-family:var(--font-display);font-size:18px;font-weight:500;">Rezepte</div>
<div style="width:26px;height:26px;border-radius:4px;border:1px solid var(--color-border);background:var(--color-surface);display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--color-text-muted);">+</div>
</div>
<div style="padding:6px 12px;border-bottom:1px solid var(--color-border);">
<input style="width:100%;font-family:var(--font-sans);font-size:11px;padding:6px 8px;border-radius:var(--radius-md);border:1px solid var(--color-border);background:var(--color-surface);color:var(--color-text-muted);outline:none;" type="text" placeholder="🔍 Rezept suchen…" readonly/>
</div>
<div class="rc-list">
<div class="rc-card">
<div class="rc-name">Spaghetti Carbonara</div>
<div class="rc-tags"><span class="badge badge-m">20 min</span> <span class="badge badge-g">Einfach</span> <span class="badge badge-m">Nudeln</span></div>
<div class="rc-acts"><button class="rc-cook">🍳 Jetzt kochen</button><button class="rc-plan">📅 Zur Woche +</button></div>
</div>
<div class="rc-card">
<div class="rc-name">Mushroom Risotto</div>
<div class="rc-tags"><span class="badge badge-m">50 min</span> <span class="badge badge-y">Mittel</span> <span class="badge badge-g">Vegetarisch</span></div>
<div class="rc-acts"><button class="rc-cook">🍳 Jetzt kochen</button><button class="rc-plan">📅 Zur Woche +</button></div>
</div>
<div class="rc-card">
<div class="rc-name">Lachsfilet mit Gemüse</div>
<div class="rc-tags"><span class="badge badge-m">25 min</span> <span class="badge badge-g">Einfach</span> <span class="badge badge-g">Fisch</span></div>
<div class="rc-acts"><button class="rc-cook">🍳 Jetzt kochen</button><button class="rc-plan">📅 Zur Woche +</button></div>
</div>
</div>
<div class="mbt">
<div class="mt-i"><div class="mt-ic">📅</div><span class="mt-l">Planer</span></div>
<div class="mt-i a"><div class="mt-ic">📖</div><span class="mt-l">Rezepte</span></div>
<div class="mt-i"><div class="mt-ic">🛒</div><span class="mt-l">Einkauf</span></div>
<div class="mt-i"><div class="mt-ic">⚙️</div><span class="mt-l">Settings</span></div>
</div>
</div>
</div>
</div>
<div class="prev-col">
<div class="bp-lbl">Desktop · Recipe grid</div>
<div class="desk" style="min-height:400px;">
<div class="dsb">
<div class="dsb-logo"><div class="dsb-lm"><div class="dsb-ic">🥗</div><div class="dsb-nm">Mealplan</div></div><div class="dsb-sub">Smith household</div></div>
<div class="dsb-nav">
<div class="dsb-ni"><span class="dsb-nc">📅</span> Planer</div>
<div class="dsb-ni a"><span class="dsb-nc">📖</span> Rezepte</div>
<div class="dsb-ni"><span class="dsb-nc">🛒</span> Einkauf</div>
</div>
</div>
<div class="d-main">
<div style="padding:10px 18px;border-bottom:1px solid var(--color-border);display:flex;justify-content:space-between;align-items:center;">
<div style="font-family:var(--font-display);font-size:18px;font-weight:500;letter-spacing:-.02em;">Rezepte</div>
<div style="display:flex;gap:8px;align-items:center;">
<input style="font-family:var(--font-sans);font-size:11px;padding:5px 8px;border-radius:var(--radius-md);border:1px solid var(--color-border);background:var(--color-surface);color:var(--color-text-muted);outline:none;width:160px;" type="text" placeholder="🔍 Suchen…" readonly/>
<button style="font-family:var(--font-sans);font-size:11px;font-weight:500;padding:5px 12px;border-radius:var(--radius-md);background:var(--green);color:#fff;border:none;">+ Neu</button>
</div>
</div>
<div style="flex:1;overflow-y:auto;">
<div class="d-rcgrid">
<div class="d-rccard">
<div class="d-rcname">Spaghetti Carbonara</div>
<div class="d-rcdesc">Cremige römische Pasta — kein Sahne, nur Ei und Guanciale.</div>
<div class="d-rctags"><span class="badge badge-m">20 min</span> <span class="badge badge-g">Einfach</span> <span class="badge badge-m">Nudeln</span></div>
<div class="d-rcacts"><button class="d-rccook">🍳 Jetzt kochen</button><button class="d-rcplan">📅 Zur Woche +</button></div>
</div>
<div class="d-rccard">
<div class="d-rcname">Mushroom Risotto</div>
<div class="d-rcdesc">Cremiges Risotto mit Pilzen, Parmesan und frischem Thymian.</div>
<div class="d-rctags"><span class="badge badge-m">50 min</span> <span class="badge badge-y">Mittel</span> <span class="badge badge-g">Vegetarisch</span></div>
<div class="d-rcacts"><button class="d-rccook">🍳 Jetzt kochen</button><button class="d-rcplan">📅 Zur Woche +</button></div>
</div>
<div class="d-rccard">
<div class="d-rcname">Lachsfilet mit Gemüse</div>
<div class="d-rcdesc">Knusprig gebratener Lachs auf Ofengemüse.</div>
<div class="d-rctags"><span class="badge badge-m">25 min</span> <span class="badge badge-g">Einfach</span> <span class="badge badge-g">Fisch</span></div>
<div class="d-rcacts"><button class="d-rccook">🍳 Jetzt kochen</button><button class="d-rcplan">📅 Zur Woche +</button></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="grid2" style="margin-top:16px;">
<div class="note hl">
<h4>Two actions, two visual weights</h4>
<p>"Jetzt kochen" is filled green (primary). "Zur Woche +" is green-tint bg / green-dark text / green-light border (secondary). Same green family — clearly related, but different priority. Both are 10px mobile / 11px desktop, weight 500, radius-md.</p>
</div>
<div class="note">
<h4>Navigation targets</h4>
<p>"Jetzt kochen" → navigate to /cook/{recipeId} (J3, no sheet or confirmation). "Zur Woche +" → open C6 day picker (bottom sheet mobile, panel transformation desktop). The Recipes tab stays active.</p>
</div>
</div>
<div class="agent">
<h4>C5 · Recipe quick actions — implementation</h4>
<pre>/* Add two buttons to every recipe card in B1 (Recipes tab).
* Position: below the tags row, above any description/ingredients.
* "Jetzt kochen": navigate to /cook/{recipeId}. No confirmation. Primary style.
* "Zur Woche +": open C6 day-picker component. Secondary style.
* Both always visible — not hover-gated. Touch-capable desktops need always-visible actions.
* Mobile: equal-width flex row, font-size 10px, padding 5px 6px.
* Desktop grid: equal-width flex row, font-size 11px, padding 7px. */</pre>
<table class="at"><thead><tr><th>Token</th><th>Cook btn</th><th>Plan btn</th></tr></thead><tbody>
<tr><td>Background</td><td>var(--green)</td><td>var(--green-tint)</td></tr>
<tr><td>Text color</td><td>#fff</td><td>var(--green-dark)</td></tr>
<tr><td>Border</td><td>none</td><td>1px var(--green-light)</td></tr>
<tr><td>Font size mobile</td><td>10px</td><td>10px</td></tr>
<tr><td>Font size desktop</td><td>11px</td><td>11px</td></tr>
<tr><td>Radius</td><td>var(--radius-md)</td><td>var(--radius-md)</td></tr>
</tbody></table>
</div>
</div>
<!-- ═══ C6: DAY PICKER ═══ -->
<div class="scr" id="c6">
<div class="scr-head"><h3>Day picker — add from recipe</h3><span class="scr-id">C6</span></div>
<div class="scr-desc">Entry: tap "Zur Woche +" on any recipe card (C5). Recipe is already known; user picks the target day. Mobile: compact bottom sheet (~55vh) with week strip. Desktop: detail panel transforms to day picker. Empty slots have dashed green border to invite selection. Selecting a filled slot shows an inline replace warning — no modal dialog.</div>
<div class="scr-var"><strong>V1 · Empty slot selected</strong> (green confirm) · <strong>V2 · Filled slot selected</strong> (orange replace confirm)</div>
<div class="section">
<div class="section-title">Breakpoint 1 — Mobile · Both variants</div>
<div class="previews">
<!-- V1: empty slot -->
<div class="prev-col">
<div class="bp-lbl">V1 · Empty slot (Sa)</div>
<div class="phone">
<div class="pst"><b>9:41</b><span>●●● WiFi 🔋</span></div>
<div class="pb">
<div style="opacity:.28;">
<div style="padding:8px 14px;border-bottom:1px solid var(--color-border);font-family:var(--font-display);font-size:16px;font-weight:500;">Rezepte</div>
<div style="padding:8px 12px;">
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:9px 11px;margin-bottom:5px;font-family:var(--font-display);font-size:12px;">Spaghetti Carbonara</div>
<div style="background:var(--green-tint);border:2px solid var(--green);border-radius:var(--radius-lg);padding:9px 11px;font-family:var(--font-display);font-size:12px;">Mushroom Risotto</div>
</div>
</div>
<div class="sheet-host">
<div class="sheet-bg"></div>
<div class="sheet" style="max-height:55%;">
<div class="sheet-handle"></div>
<div class="sheet-hd">
<div>
<div class="sheet-ht">Mushroom Risotto</div>
<div class="sheet-sub">Zu welchem Tag hinzufügen?</div>
</div>
<div class="sheet-x">×</div>
</div>
<div style="padding:6px 12px 2px;display:flex;justify-content:space-between;align-items:center;">
<span style="font-size:10px;font-weight:500;color:var(--color-text);">31 März 6 Apr</span>
<div style="display:flex;gap:3px;">
<span style="font-size:9px;padding:2px 5px;border-radius:3px;background:var(--color-subtle);border:1px solid var(--color-border);color:var(--color-text-muted);"></span>
<span style="font-size:9px;padding:2px 5px;border-radius:3px;background:var(--color-subtle);border:1px solid var(--color-border);color:var(--color-text-muted);"></span>
</div>
</div>
<div class="dp-strip">
<div class="dp-chip filled"><span class="dp-ca">Mo</span><span class="dp-cn">31</span><div class="dp-dot"></div></div>
<div class="dp-chip filled today"><span class="dp-ca">Di</span><span class="dp-cn">1</span><div class="dp-dot"></div></div>
<div class="dp-chip filled"><span class="dp-ca">Mi</span><span class="dp-cn">2</span><div class="dp-dot"></div></div>
<div class="dp-chip filled"><span class="dp-ca">Do</span><span class="dp-cn">3</span><div class="dp-dot"></div></div>
<div class="dp-chip filled"><span class="dp-ca">Fr</span><span class="dp-cn">4</span><div class="dp-dot"></div></div>
<div class="dp-chip sel-empty"><span class="dp-ca">Sa</span><span class="dp-cn">5</span><div class="dp-dot"></div></div>
<div class="dp-chip filled"><span class="dp-ca">So</span><span class="dp-cn">6</span><div class="dp-dot"></div></div>
</div>
<div class="dp-confirm">
<button class="dp-btn">Zu Samstag hinzufügen</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- V2: filled slot -->
<div class="prev-col">
<div class="bp-lbl">V2 · Filled slot (Mi) — ersetzen?</div>
<div class="phone">
<div class="pst"><b>9:41</b><span>●●● WiFi 🔋</span></div>
<div class="pb">
<div style="opacity:.28;">
<div style="padding:8px 14px;border-bottom:1px solid var(--color-border);font-family:var(--font-display);font-size:16px;font-weight:500;">Rezepte</div>
<div style="padding:8px 12px;">
<div style="background:var(--green-tint);border:2px solid var(--green);border-radius:var(--radius-lg);padding:9px 11px;font-family:var(--font-display);font-size:12px;">Mushroom Risotto</div>
</div>
</div>
<div class="sheet-host">
<div class="sheet-bg"></div>
<div class="sheet" style="max-height:60%;">
<div class="sheet-handle"></div>
<div class="sheet-hd">
<div>
<div class="sheet-ht">Mushroom Risotto</div>
<div class="sheet-sub">Zu welchem Tag hinzufügen?</div>
</div>
<div class="sheet-x">×</div>
</div>
<div style="padding:6px 12px 2px;display:flex;justify-content:space-between;align-items:center;">
<span style="font-size:10px;font-weight:500;color:var(--color-text);">31 März 6 Apr</span>
<div style="display:flex;gap:3px;">
<span style="font-size:9px;padding:2px 5px;border-radius:3px;background:var(--color-subtle);border:1px solid var(--color-border);color:var(--color-text-muted);"></span>
<span style="font-size:9px;padding:2px 5px;border-radius:3px;background:var(--color-subtle);border:1px solid var(--color-border);color:var(--color-text-muted);"></span>
</div>
</div>
<div class="dp-strip">
<div class="dp-chip filled"><span class="dp-ca">Mo</span><span class="dp-cn">31</span><div class="dp-dot"></div></div>
<div class="dp-chip filled today"><span class="dp-ca">Di</span><span class="dp-cn">1</span><div class="dp-dot"></div></div>
<div class="dp-chip sel-filled"><span class="dp-ca">Mi</span><span class="dp-cn">2</span><div class="dp-dot"></div></div>
<div class="dp-chip filled"><span class="dp-ca">Do</span><span class="dp-cn">3</span><div class="dp-dot"></div></div>
<div class="dp-chip filled"><span class="dp-ca">Fr</span><span class="dp-cn">4</span><div class="dp-dot"></div></div>
<div class="dp-chip empty"><span class="dp-ca">Sa</span><span class="dp-cn">5</span><div class="dp-dot"></div></div>
<div class="dp-chip filled"><span class="dp-ca">So</span><span class="dp-cn">6</span><div class="dp-dot"></div></div>
</div>
<div class="dp-warn">Ersetzt <strong>Tomaten-Pasta</strong> am Mittwoch. Undo möglich.</div>
<div class="dp-confirm">
<button class="dp-btn replace">Mittwoch ersetzen</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:12px;align-self:flex-start;max-width:220px;">
<div class="note hl">
<h4>Slot states</h4>
<p>Empty: dashed green-light border + green-tint bg (invites selection). Filled: solid color-border + surface bg + green dot. Today: yellow-tint border. Selected empty: 2px green-dark. Selected filled: 2px orange-dark + orange-tint → shows replace warning.</p>
</div>
<div class="note warn">
<h4>No replace dialog</h4>
<p>The replace warning appears inline below the week strip. The confirm button turns orange. One tap replaces. An undo toast appears for 4 seconds. No modal dialog is shown — consistent with J4 swap.</p>
</div>
<div class="note">
<h4>Week navigation</h4>
<p> buttons allow browsing to next/prev week if the current week has no empty slots. Default: shows current week.</p>
</div>
</div>
</div>
</div>
<div class="section">
<div class="section-title">Breakpoint 3 — Desktop · Panel day-picker mode</div>
<div style="overflow-x:auto;margin-bottom:20px;">
<div class="desk" style="min-height:440px;">
<div class="dsb">
<div class="dsb-logo"><div class="dsb-lm"><div class="dsb-ic">🥗</div><div class="dsb-nm">Mealplan</div></div><div class="dsb-sub">Smith household</div></div>
<div class="dsb-nav">
<div class="dsb-ni"><span class="dsb-nc">📅</span> Planer</div>
<div class="dsb-ni a"><span class="dsb-nc">📖</span> Rezepte</div>
<div class="dsb-ni"><span class="dsb-nc">🛒</span> Einkauf</div>
</div>
</div>
<div class="d-main">
<div style="padding:10px 18px;border-bottom:1px solid var(--color-border);display:flex;justify-content:space-between;align-items:center;">
<div style="font-family:var(--font-display);font-size:18px;font-weight:500;letter-spacing:-.02em;">Rezepte</div>
<input style="font-family:var(--font-sans);font-size:11px;padding:5px 8px;border-radius:var(--radius-md);border:1px solid var(--color-border);background:var(--color-surface);color:var(--color-text-muted);outline:none;width:160px;" type="text" placeholder="🔍 Suchen…" readonly/>
</div>
<div style="flex:1;display:flex;overflow:hidden;">
<!-- Recipe grid (dimmed, active card highlighted) -->
<div style="flex:1;overflow-y:auto;">
<div class="d-rcgrid">
<div class="d-rccard" style="opacity:.45;">
<div class="d-rcname">Spaghetti Carbonara</div>
<div class="d-rctags"><span class="badge badge-m">20 min</span> <span class="badge badge-g">Einfach</span></div>
<div class="d-rcacts"><button class="d-rccook">🍳 Kochen</button><button class="d-rcplan">📅 Zur Woche +</button></div>
</div>
<div class="d-rccard" style="border:2px solid var(--green);background:var(--green-tint);">
<div class="d-rcname">Mushroom Risotto</div>
<div class="d-rcdesc">Cremiges Risotto mit Pilzen und Parmesan.</div>
<div class="d-rctags"><span class="badge badge-m">50 min</span> <span class="badge badge-y">Mittel</span> <span class="badge badge-g">Vegetarisch</span></div>
<div class="d-rcacts">
<button class="d-rccook">🍳 Kochen</button>
<button style="flex:1;font-family:var(--font-sans);font-size:11px;font-weight:500;padding:7px;border-radius:var(--radius-md);background:var(--green);color:#fff;border:none;text-align:center;">📅 Tag wählen…</button>
</div>
</div>
<div class="d-rccard" style="opacity:.45;">
<div class="d-rcname">Lachsfilet</div>
<div class="d-rctags"><span class="badge badge-m">25 min</span> <span class="badge badge-g">Einfach</span></div>
<div class="d-rcacts"><button class="d-rccook">🍳 Kochen</button><button class="d-rcplan">📅 Zur Woche +</button></div>
</div>
</div>
</div>
<!-- Detail panel: day picker mode -->
<div class="d-dp">
<div class="d-dph">
<div><div class="d-dpd">Mushroom Risotto</div><div class="d-dpdt">Tag für diese Woche wählen</div></div>
<div class="d-dpx">×</div>
</div>
<!-- Mini week strip in panel -->
<div style="padding:8px 10px 4px;">
<div style="font-size:9px;font-weight:500;color:var(--color-text-muted);margin-bottom:5px;">31 März 6 Apr</div>
<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:3px;">
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 2px;border-radius:4px;border:1px solid var(--color-border);background:var(--color-surface);cursor:pointer;"><span style="font-size:6px;font-weight:500;text-transform:uppercase;color:var(--color-text-muted);">Mo</span><span style="font-size:9px;font-weight:500;">31</span><div style="width:3px;height:3px;border-radius:50%;background:var(--green);margin-top:1px;"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 2px;border-radius:4px;border:1px solid var(--yellow);background:var(--yellow-tint);cursor:pointer;"><span style="font-size:6px;font-weight:500;text-transform:uppercase;color:var(--yellow-text);">Di</span><span style="font-size:9px;font-weight:500;">1</span><div style="width:3px;height:3px;border-radius:50%;background:var(--yellow-text);margin-top:1px;"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 2px;border-radius:4px;border:1px solid var(--color-border);background:var(--color-surface);cursor:pointer;"><span style="font-size:6px;font-weight:500;text-transform:uppercase;color:var(--color-text-muted);">Mi</span><span style="font-size:9px;font-weight:500;">2</span><div style="width:3px;height:3px;border-radius:50%;background:var(--green);margin-top:1px;"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 2px;border-radius:4px;border:1px solid var(--color-border);background:var(--color-surface);cursor:pointer;"><span style="font-size:6px;font-weight:500;text-transform:uppercase;color:var(--color-text-muted);">Do</span><span style="font-size:9px;font-weight:500;">3</span><div style="width:3px;height:3px;border-radius:50%;background:var(--green);margin-top:1px;"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 2px;border-radius:4px;border:1px solid var(--color-border);background:var(--color-surface);cursor:pointer;"><span style="font-size:6px;font-weight:500;text-transform:uppercase;color:var(--color-text-muted);">Fr</span><span style="font-size:9px;font-weight:500;">4</span><div style="width:3px;height:3px;border-radius:50%;background:var(--green);margin-top:1px;"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 2px;border-radius:4px;border:2px solid var(--green-dark);background:var(--green-tint);cursor:pointer;"><span style="font-size:6px;font-weight:500;text-transform:uppercase;color:var(--green-deeper);">Sa</span><span style="font-size:9px;font-weight:500;">5</span><div style="width:3px;height:3px;border-radius:50%;background:transparent;margin-top:1px;"></div></div>
<div style="display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 2px;border-radius:4px;border:1px solid var(--color-border);background:var(--color-surface);cursor:pointer;"><span style="font-size:6px;font-weight:500;text-transform:uppercase;color:var(--color-text-muted);">So</span><span style="font-size:9px;font-weight:500;">6</span><div style="width:3px;height:3px;border-radius:50%;background:var(--green);margin-top:1px;"></div></div>
</div>
</div>
<!-- Selected day + confirm -->
<div style="padding:6px 10px;border-top:1px solid var(--color-subtle);">
<div style="font-size:9px;color:var(--color-text-muted);margin-bottom:5px;">Samstag, 5. April · leer</div>
<button style="width:100%;font-family:var(--font-sans);font-size:11px;font-weight:500;padding:7px;border-radius:var(--radius-md);background:var(--green);color:#fff;border:none;text-align:center;">Zu Samstag hinzufügen</button>
</div>
<!-- Variety preview (desktop only) -->
<div style="padding:8px 10px;border-top:1px solid var(--color-subtle);">
<div style="font-size:8px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:5px;">Variety-Vorschau</div>
<div style="background:var(--green-tint);border:1px solid var(--green-light);border-radius:var(--radius-md);padding:6px 8px;font-size:10px;color:var(--green-dark);">↑ Score: 8 → 9 · Neues Protein</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="grid3">
<div class="note hl"><h4>Active card highlight</h4><p>The card whose day picker is open: 2px green border, green-tint bg, "Zur Woche +" button becomes green filled "Tag wählen…". Other cards at 45% opacity to keep focus on the active recipe.</p></div>
<div class="note"><h4>Variety preview (desktop only)</h4><p>Below the confirm button the panel shows the projected score change and reason: "↑ Score: 8 → 9 · Neues Protein". Omitted on mobile to keep the sheet compact. Calls GET /api/variety/preview?add={recipeId}&amp;date={date}.</p></div>
<div class="note warn"><h4>Replace on desktop</h4><p>Same V2 pattern: clicking a filled day chip turns the confirm button orange and shows inline warning "Ersetzt [recipe] am [day]." Panel width is enough to show both elements without overlap.</p></div>
</div>
</div>
<div class="agent">
<h4>C6 · Day picker — implementation</h4>
<pre>/* Entry: tap "Zur Woche +" on recipe card. Recipe ID is known; date is unknown.
* Mobile: compact bottom sheet (~55vh). Background: recipe list at 28% opacity.
* Sheet: drag handle + recipe name header + week label + week nav + 7-day strip + confirm.
*
* DAY CHIP STATES (7 chips, current week):
* .empty → dashed green-light border, green-tint bg (invite selection)
* .filled → solid color-border, surface bg, green dot below date number
* .today → solid yellow border, yellow-tint bg, yellow-text label
* .sel-empty → 2px green-dark border, green-tint bg → confirm btn = green
* .sel-filled → 2px orange-dark border, orange-tint bg → show dp-warn → confirm btn = orange
*
* Confirm empty: PATCH /api/week-plan/{weekId}/slots/{date} {recipe_id}
* → dismiss sheet → undo toast 4s "Rückgängig"
* Confirm filled: same PATCH (server replaces existing recipe_id)
* → dismiss sheet → undo toast 4s "Rückgängig"
*
* Desktop panel state: 'day-picker'
* Active recipe card: 2px green border, green-tint bg, button text "Tag wählen…" (green filled)
* Other cards: opacity 0.45
* Panel adds variety-preview section below confirm: GET /api/variety/preview?add={id}&date={date}
* Confirm → panel transitions to day-detail for newly filled date.
*
* Week navigation: loads prev/next week's slots from GET /api/week-plan/{weekId±1}
* Default: current week. If current week fully filled, auto-advance to next week. */</pre>
<table class="at"><thead><tr><th>State</th><th>Border</th><th>Background</th><th>Confirm btn</th></tr></thead><tbody>
<tr><td>empty</td><td>dashed green-light</td><td>green-tint</td><td></td></tr>
<tr><td>filled</td><td>solid color-border</td><td>color-surface</td><td></td></tr>
<tr><td>today</td><td>solid yellow</td><td>yellow-tint</td><td></td></tr>
<tr><td>sel-empty</td><td>2px green-dark</td><td>green-tint</td><td>green</td></tr>
<tr><td>sel-filled</td><td>2px orange-dark</td><td>orange-tint</td><td>orange + dp-warn</td></tr>
</tbody></table>
</div>
</div>
<!-- ═══ LLM INSTRUCTIONS ═══ -->
<div class="llm">
<h2>LLM Implementation Notes — J2 Add to Plan (C4C6)</h2>
<h3>1. Screens and entry points</h3>
<p><strong>C4</strong>: User knows the day, not the recipe. Entry: "+" or empty slot tap in C1. <strong>C5</strong>: Recipe card in B1 gains two quick action buttons. <strong>C6</strong>: User knows the recipe, not the day. Entry: "Zur Woche +" on C5 card. All three flows share the same backend write: <code>PATCH /api/week-plan/{weekId}/slots/{date} { recipe_id }</code>.</p>
<h3>2. C4 vs J4 — different sort, same sheet pattern</h3>
<p>Both use a bottom sheet over dimmed content. The difference: J4 swap suggestions are sorted <strong>effort ASC</strong> (easiest first, because mid-week changes happen under stress). C4 suggestions are sorted <strong>variety_delta DESC</strong> (best variety impact first, because this is deliberate weekly planning).</p>
<h3>3. Panel state machine (desktop)</h3>
<p>The 216px right panel can be in four states: <strong>idle</strong> (no selection) → <strong>day-detail</strong> (filled day selected in C1) → <strong>recipe-picker</strong> (C4 mode) → <strong>day-picker</strong> (C6 mode). The "×" close button always returns to the previous state. Successful pick transitions to day-detail for the affected date.</p>
<h3>4. Tap counts</h3>
<ul>
<li><strong>C4 via empty slot:</strong> 2 taps (slot → pick recipe)</li>
<li><strong>C4 via "+" button:</strong> 2 taps ("+" → pick recipe, day pre-selected as next empty)</li>
<li><strong>C6 empty slot:</strong> 2 taps ("Zur Woche +" → pick day)</li>
<li><strong>C6 filled slot:</strong> 3 taps ("Zur Woche +" → pick day → confirm replace)</li>
</ul>
<h3>5. Undo</h3>
<p>All flows use <code>DELETE /api/week-plan/{weekId}/slots/{date}</code> when "Rückgängig" is tapped in the 4-second toast. Toast is dismissed on navigation. On desktop, no toast for C4 (panel shows result immediately with Swap option in the day-detail view).</p>
<h3>6. C5 "Jetzt kochen" — no back-stack issue</h3>
<p>Navigating to <code>/cook/{recipeId}</code> from the recipe list is a standard route push. The recipe list is available via the back button or bottom nav. Do not open cook mode in a sheet or modal — it is a full-screen experience per J3 spec.</p>
</div>
</div>
</body>
</html>