Files
mealprep/specs/frontend/j1-add-recipe.html
Marcel Raddatz b36d4c731d Add frontend journey specs with visual previews and LLM instructions
Six self-contained HTML documents, one per user journey, each combining
mobile/desktop previews with machine-readable implementation guides:

- j1-add-recipe.html (B1, B3)
- j2-plan-the-week.html (C1, C2, C3)
- j3-cook-tonight.html (B2, B4)
- j4-adapt-on-the-fly.html (swap trigger, C2 swap)
- j5-shopping-list.html (D1)
- j6-household-setup.html (A1, A2, A3/D3, A4)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 12:17:47 +02:00

395 lines
40 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>J1 — Add a Recipe</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;padding-bottom:28px;border-bottom:1px solid var(--color-border);margin-bottom:48px;background:var(--color-page);margin:-48px -40px 48px;padding:48px 40px 28px;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;}
.pill{display:inline-block;padding:2px 8px;border-radius:var(--radius-sm);font-size:10px;font-weight:500;letter-spacing:.05em;background:var(--green-tint);color:var(--green-dark);}
/* 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;}
.prose{font-size:13px;color:var(--color-text-muted);line-height:1.65;max-width:720px;margin-bottom:20px;}
/* Journey headers */
.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-p{background:var(--purple-tint);border:1px solid #CECBF6;}.jh-p .jn{color:var(--purple);}.jh-p p,.jh-p .fl{color:var(--purple-dark);}
.jh-g{background:var(--green-tint);border:1px solid var(--green-light);}.jh-g .jn{color:var(--green);}.jh-g p,.jh-g .fl{color:var(--green-dark);}
.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);}
.jh-o{background:var(--orange-tint);border:1px solid #FBCDA4;}.jh-o .jn{color:var(--orange);}.jh-o p,.jh-o .fl{color:var(--orange-dark);}
.jh-b{background:var(--blue-tint);border:1px solid var(--blue-light);}.jh-b .jn{color:var(--blue);}.jh-b p,.jh-b .fl{color:var(--blue-dark);}
/* 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 - 320px */
.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;overflow-y:auto;display:flex;flex-direction:column;}
/* Desktop frame - 1040px */
.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;}
/* Shared nav components */
.mtb{padding:10px 16px;background:var(--color-page);border-bottom:1px solid var(--color-border);display:flex;justify-content:space-between;align-items:center;}
.mtb-t{font-family:var(--font-display);font-size:20px;font-weight:500;letter-spacing:-.02em;}
.mi{width:32px;height:32px;border-radius:var(--radius-md);border:1px solid var(--color-border);background:var(--color-surface);display:flex;align-items:center;justify-content:center;font-size:13px;color:var(--color-text-muted);flex-shrink:0;}.mi.gn{background:var(--green);border-color:var(--green);color:#fff;}
.mbt{border-top:1px solid var(--color-border);background:var(--color-surface);padding:8px 16px 28px;display:flex;justify-content:space-around;}
.mt-i{display:flex;flex-direction:column;align-items:center;gap:2px;}.mt-ic{width:20px;height:20px;border-radius:4px;background:var(--color-subtle);display:flex;align-items:center;justify-content:center;font-size:11px;}.mt-i.a .mt-ic{background:var(--green-tint);}.mt-l{font-size:9px;font-weight:500;color:var(--color-text-muted);}.mt-i.a .mt-l{color:var(--green-dark);}
/* Desktop sidebar - 224px per nav-spec */
.dsb{width:224px;flex-shrink:0;background:var(--color-surface);border-right:1px solid var(--color-border);display:flex;flex-direction:column;}
.dsb-logo{padding:20px 16px 16px;border-bottom:1px solid var(--color-border);}
.dsb-lm{display:flex;align-items:center;gap:8px;margin-bottom:2px;}.dsb-ic{width:24px;height:24px;border-radius:5px;background:var(--green);display:flex;align-items:center;justify-content:center;font-size:12px;}.dsb-nm{font-family:var(--font-display);font-size:16px;font-weight:500;letter-spacing:-.02em;}.dsb-sub{font-size:10px;color:var(--color-text-muted);padding-left:32px;}
.dsb-nav{padding:12px 10px;flex:1;}.dsb-nl{font-size:8px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);padding:0 8px;margin-bottom:4px;}.dsb-ni{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:var(--radius-md);font-size:13px;color:var(--color-text-muted);margin-bottom:2px;cursor:default;}.dsb-ni.a{background:var(--green-tint);color:var(--green-dark);font-weight:500;}.dsb-nc{font-size:13px;width:18px;text-align:center;}
.dm{flex:1;display:flex;flex-direction:column;min-width:0;}
.dtb{padding:12px 24px;border-bottom:1px solid var(--color-border);display:flex;justify-content:space-between;align-items:center;flex-shrink:0;}
.dtb-t{font-family:var(--font-display);font-size:18px;font-weight:500;letter-spacing:-.02em;}
.dtb-r{display:flex;align-items:center;gap:8px;}
.dab{font-family:var(--font-sans);font-size:13px;font-weight:500;padding:7px 16px;border-radius:var(--radius-md);background:var(--green);color:#fff;border:none;}
.dab-b{background:var(--blue);}
/* Shared form */
.fi{width:100%;font-family:var(--font-sans);font-size:14px;padding:10px 12px;border-radius:var(--radius-md);border:1px solid var(--color-border);background:var(--color-page);color:var(--color-text);outline:none;}
.fl{font-size:12px;font-weight:500;color:var(--color-text);margin-bottom:6px;display:block;}
.fg{margin-bottom:16px;}
.bp{font-family:var(--font-sans);font-size:14px;font-weight:500;padding:12px 24px;border-radius:var(--radius-md);background:var(--green);color:#fff;border:none;cursor:pointer;width:100%;}
.bg{font-family:var(--font-sans);font-size:13px;font-weight:500;padding:10px 20px;border-radius:var(--radius-md);background:var(--color-subtle);color:var(--color-text-muted);border:1px solid var(--color-border);cursor:pointer;}
/* Tags */
.tc{display:inline-flex;font-size:12px;font-weight:500;padding:6px 12px;border-radius:20px;border:1px solid var(--color-border);background:var(--color-surface);color:var(--color-text-muted);cursor:pointer;margin:0 4px 4px 0;user-select:none;}.tc.s{background:var(--green-tint);color:var(--green-dark);border-color:var(--green-light);}
.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);}
/* Ingredient rows */
.ir{display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid var(--color-subtle);}.ir:last-child{border-bottom:none;}.ir-q{font-family:var(--font-mono);font-size:12px;color:var(--color-text-muted);width:55px;flex-shrink:0;text-align:right;}.ir-n{font-size:13px;color:var(--color-text);flex:1;}.ir-x{font-size:14px;color:var(--color-border);cursor:pointer;width:20px;text-align:center;}
/* Checklist */
.ck{display:flex;align-items:center;gap:10px;padding:10px 0;border-bottom:1px solid var(--color-subtle);cursor:pointer;}.ck:last-child{border-bottom:none;}.ck-b{width:22px;height:22px;border-radius:4px;border:2px solid var(--color-border);flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:11px;}.ck.d .ck-b{background:var(--green);border-color:var(--green);color:#fff;}.ck-c{flex:1;}.ck-n{font-size:14px;color:var(--color-text);}.ck.d .ck-n{text-decoration:line-through;color:var(--color-text-muted);}.ck-s{font-size:10px;color:var(--color-text-muted);}.ck-q{font-family:var(--font-mono);font-size:12px;color:var(--color-text-muted);flex-shrink:0;}
/* Suggestion cards */
.sg{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:12px;margin-bottom:8px;display:flex;align-items:center;gap:10px;}.sg-r{font-family:var(--font-display);font-size:16px;font-weight:300;color:var(--color-text-muted);width:20px;text-align:center;flex-shrink:0;}.sg-b{flex:1;}.sg-n{font-family:var(--font-display);font-size:13px;font-weight:400;color:var(--color-text);margin-bottom:2px;}.sg-i{font-size:10px;color:var(--color-text-muted);}.sg-w{font-size:9px;color:var(--green-dark);background:var(--green-tint);padding:2px 6px;border-radius:3px;display:inline-block;margin-top:3px;}.sg-p{font-size:11px;font-weight:500;color:var(--green);flex-shrink:0;}
/* Eyebrow labels */
.eye{font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);}
/* Agent table */
.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;}
/* LLM instruction box */
.llm{background:var(--color-page);border:2px solid var(--green);border-radius:var(--radius-xl);padding:32px 40px;margin-top:64px;}
.llm h2{font-family:var(--font-display);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-bottom:8px;color:var(--green-dark);}
.llm h3{font-size:14px;font-weight:600;margin:20px 0 8px;color:var(--color-text);}
.llm p,.llm li{font-size:13px;color:var(--color-text-muted);line-height:1.65;}
.llm ul,.llm ol{padding-left:20px;margin-bottom:12px;}
.llm li{margin-bottom:4px;}
.llm code{font-family:var(--font-mono);font-size:11px;background:var(--color-surface);padding:1px 5px;border-radius:3px;}
.llm table{width:100%;border-collapse:collapse;margin:12px 0;font-size:12px;}
.llm th,.llm td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--color-border);}
.llm th{font-weight:500;color:var(--color-text);font-size:11px;text-transform:uppercase;letter-spacing:.05em;}
.llm td{color:var(--color-text-muted);}
@media(max-width:900px){.doc{padding:24px 16px 80px;}}
</style>
</head>
<body>
<div class="doc">
<div class="jh jh-g">
<div class="jn">J1</div>
<div><h2>Add a recipe</h2><p>Save recipe with ingredients, steps, tags, and hero image.</p><div class="fl">B1 → B3 → B1 · Planner · Min: effort + 1 category tag</div></div>
</div>
<!-- ═══ B1 RECIPE LIBRARY ═══ -->
<div class="scr" id="b1">
<div class="scr-head"><h3>Recipe library</h3><span class="scr-id">B1</span></div>
<div class="scr-desc">Browse all recipes. Desktop: app sidebar + topbar with search bar and filter chips + 4-column card grid. The grid fills the content area naturally — not wrapped in an additional card or panel.</div>
<div class="scr-var"><strong>V2 · Card grid</strong> — mobile 2-col, desktop sidebar + 4-col with filters</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Mobile · 320px</div>
<div class="phone">
<div class="pst"><b>9:41</b><span>●●● WiFi 🔋</span></div>
<div class="pb">
<div class="mtb"><div class="mtb-t">Recipes</div><div style="display:flex;gap:6px;"><div class="mi">🔍</div><div class="mi gn">+</div></div></div>
<div style="padding:10px 12px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;"><div style="height:64px;background:var(--green-tint);display:flex;align-items:center;justify-content:center;font-size:28px;">🍝</div><div style="padding:8px 10px;"><div style="font-family:var(--font-display);font-size:12px;margin-bottom:3px;">Tomato pasta</div><div style="font-size:9px;color:var(--color-text-muted);">45 min · Easy</div></div></div>
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;"><div style="height:64px;background:var(--yellow-tint);display:flex;align-items:center;justify-content:center;font-size:28px;">🍗</div><div style="padding:8px 10px;"><div style="font-family:var(--font-display);font-size:12px;margin-bottom:3px;">Chicken stir-fry</div><div style="font-size:9px;color:var(--color-text-muted);">25 min · Easy</div></div></div>
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;"><div style="height:64px;background:var(--blue-tint);display:flex;align-items:center;justify-content:center;font-size:28px;">🐟</div><div style="padding:8px 10px;"><div style="font-family:var(--font-display);font-size:12px;margin-bottom:3px;">Salmon teriyaki</div><div style="font-size:9px;color:var(--color-text-muted);">35 min · Medium</div></div></div>
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;"><div style="height:64px;background:var(--green-tint);display:flex;align-items:center;justify-content:center;font-size:28px;">🍄</div><div style="padding:8px 10px;"><div style="font-family:var(--font-display);font-size:12px;margin-bottom:3px;">Mushroom risotto</div><div style="font-size:9px;color:var(--color-text-muted);">50 min · Medium</div></div></div>
</div>
</div>
<div class="mbt"><div class="mt-i"><div class="mt-ic">📅</div><div class="mt-l">Planner</div></div><div class="mt-i a"><div class="mt-ic">📖</div><div class="mt-l">Recipes</div></div><div class="mt-i"><div class="mt-ic">🛒</div><div class="mt-l">Shopping</div></div><div class="mt-i"><div class="mt-ic">⚙️</div><div class="mt-l">Settings</div></div></div>
</div>
</div>
</div>
<div class="prev-col">
<div class="bp-lbl">Desktop · 1040px</div>
<div class="desk">
<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 style="margin-bottom:16px;"><div class="dsb-nl">Plan</div><div class="dsb-ni"><span class="dsb-nc">📅</span>Planner</div><div class="dsb-ni a"><span class="dsb-nc">📖</span>Recipes</div><div class="dsb-ni"><span class="dsb-nc">🛒</span>Shopping</div></div><div><div class="dsb-nl">Account</div><div class="dsb-ni"><span class="dsb-nc">👨‍👩‍👧</span>Household</div><div class="dsb-ni"><span class="dsb-nc">⚙️</span>Settings</div></div></div>
</div>
<div class="dm">
<div class="dtb"><div class="dtb-t">Recipe library</div><div class="dtb-r"><input class="fi" style="width:220px;font-size:12px;padding:7px 12px;" placeholder="🔍 Search recipes…"/><button class="dab">+ Add recipe</button></div></div>
<div style="flex:1;padding:20px 24px;overflow-y:auto;">
<!-- Filter chips -->
<div style="display:flex;gap:5px;margin-bottom:16px;">
<div style="font-size:11px;font-weight:500;padding:5px 14px;border-radius:12px;background:var(--green-tint);color:var(--green-dark);">All (24)</div>
<div style="font-size:11px;font-weight:500;padding:5px 14px;border-radius:12px;border:1px solid var(--color-border);color:var(--color-text-muted);">Easy</div>
<div style="font-size:11px;font-weight:500;padding:5px 14px;border-radius:12px;border:1px solid var(--color-border);color:var(--color-text-muted);">Medium</div>
<div style="font-size:11px;font-weight:500;padding:5px 14px;border-radius:12px;border:1px solid var(--color-border);color:var(--color-text-muted);">Chicken</div>
<div style="font-size:11px;font-weight:500;padding:5px 14px;border-radius:12px;border:1px solid var(--color-border);color:var(--color-text-muted);">Fish</div>
<div style="font-size:11px;font-weight:500;padding:5px 14px;border-radius:12px;border:1px solid var(--color-border);color:var(--color-text-muted);">Veggie</div>
</div>
<!-- 4-col grid -->
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:12px;">
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;cursor:pointer;"><div style="height:100px;background:var(--green-tint);display:flex;align-items:center;justify-content:center;font-size:32px;">🍝</div><div style="padding:10px 12px;"><div style="font-family:var(--font-display);font-size:14px;font-weight:400;margin-bottom:4px;">Slow-roasted tomato pasta</div><div style="font-size:10px;color:var(--color-text-muted);margin-bottom:6px;">45 min · Easy · Last cooked 3 days ago</div><div style="display:flex;gap:3px;"><span class="badge badge-g">Vegetarian</span><span class="badge badge-y">Child-friendly</span></div></div></div>
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;"><div style="height:100px;background:var(--yellow-tint);display:flex;align-items:center;justify-content:center;font-size:32px;">🍗</div><div style="padding:10px 12px;"><div style="font-family:var(--font-display);font-size:14px;font-weight:400;margin-bottom:4px;">Chicken stir-fry</div><div style="font-size:10px;color:var(--color-text-muted);margin-bottom:6px;">25 min · Easy · 5 days ago</div><div style="display:flex;gap:3px;"><span class="badge badge-m">Chicken</span></div></div></div>
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;"><div style="height:100px;background:var(--blue-tint);display:flex;align-items:center;justify-content:center;font-size:32px;">🐟</div><div style="padding:10px 12px;"><div style="font-family:var(--font-display);font-size:14px;font-weight:400;margin-bottom:4px;">Salmon teriyaki with rice</div><div style="font-size:10px;color:var(--color-text-muted);margin-bottom:6px;">35 min · Medium · 2 weeks ago</div><div style="display:flex;gap:3px;"><span class="badge badge-m">Fish</span></div></div></div>
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;"><div style="height:100px;background:var(--green-tint);display:flex;align-items:center;justify-content:center;font-size:32px;">🍄</div><div style="padding:10px 12px;"><div style="font-family:var(--font-display);font-size:14px;font-weight:400;margin-bottom:4px;">Mushroom risotto</div><div style="font-size:10px;color:var(--color-text-muted);margin-bottom:6px;">50 min · Medium · 2 weeks ago</div><div style="display:flex;gap:3px;"><span class="badge badge-g">Vegetarian</span></div></div></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="agent">
<h4>B1 · Recipe library</h4>
<pre>/* Desktop: 224px sidebar + topbar (search 220px + Add button) + content: filter chips row + 4-col grid.
* Desktop cards are richer: 100px image area + name (Fraunces 14px) + meta + tags row.
* Mobile: 2-col, 64px image, no tags on cards (too small).
* Filter chips: from tag table. Active: green-tint. 12px border-radius.
* Grid fills the content area directly — no wrapping card.
* Card click → B2 (recipe detail). Add button → B3 (empty form). */</pre>
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
<tr class="grp"><td colspan="3">Desktop</td></tr>
<tr><td>Grid</td><td>4 columns, 12px gap, content padding 20px 24px</td><td>Cards: radius-lg, border-default, overflow hidden</td></tr>
<tr><td>Card image</td><td>100px height. hero_image_url → object-fit:cover. NULL → tint + emoji.</td><td>Tint color based on first protein tag</td></tr>
<tr><td>Card content</td><td>10px 12px padding. Name: Fraunces 14px. Meta: 10px muted. Tags: badge row.</td><td>meta shows cook time + effort + "last cooked X ago"</td></tr>
<tr><td>Filter chips</td><td>11px/500, 5px 14px pad, 12px radius</td><td>Active: green-tint bg + green-dark text. Others: border-default.</td></tr>
<tr class="grp"><td colspan="3">Mobile</td></tr>
<tr><td>Grid</td><td>2 columns, 8px gap</td><td>Cards: 64px image, 12px name, 9px meta. No tags.</td></tr>
</tbody></table>
</div>
</div>
<!-- ═══ B3 ADD/EDIT RECIPE ═══ -->
<div class="scr" id="b3">
<div class="scr-head"><h3>Add / edit recipe</h3><span class="scr-id">B3</span></div>
<div class="scr-desc">Single form for add + edit. Mobile: V1 single scroll. Desktop: V5 sidebar + topbar + split content (form left, tags + live preview panel right). The tags panel is a natural sidebar within the content area, not a floating card.</div>
<div class="scr-var"><strong>V1 mobile / V5 desktop</strong> — form left, tags + preview right on desktop</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Mobile · 320px</div>
<div class="phone">
<div class="pst"><b>9:45</b><span>●●● WiFi 🔋</span></div>
<div class="pb">
<div style="padding:10px 16px;border-bottom:1px solid var(--color-border);display:flex;justify-content:space-between;align-items:center;">
<div style="font-size:13px;color:var(--green);font-weight:500;">← Recipes</div>
<div style="font-family:var(--font-display);font-size:16px;font-weight:500;">New recipe</div>
<div style="font-size:13px;color:var(--green);font-weight:500;">Save</div>
</div>
<div style="padding:16px;">
<div style="height:56px;background:var(--color-subtle);border:1px dashed var(--color-border);border-radius:var(--radius-lg);display:flex;align-items:center;justify-content:center;margin-bottom:16px;"><div style="text-align:center;"><div style="font-size:14px;color:var(--color-border);">📷</div><div style="font-size:9px;color:var(--color-text-muted);">Add photo</div></div></div>
<div class="fg"><label class="fl">Recipe name</label><input class="fi" value="Slow-roasted tomato pasta"/></div>
<div style="display:flex;gap:8px;">
<div class="fg" style="flex:1;"><label class="fl">Serves</label><input class="fi" type="number" value="4"/></div>
<div class="fg" style="flex:1;"><label class="fl">Cook time</label><input class="fi" value="45 min"/></div>
</div>
<div class="eye" style="margin-bottom:6px;">Ingredients</div>
<div style="border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:4px 12px;margin-bottom:6px;">
<div class="ir"><div class="ir-q">500g</div><div class="ir-n">Cherry tomatoes</div><div class="ir-x">×</div></div>
<div class="ir"><div class="ir-q">300g</div><div class="ir-n">Penne pasta</div><div class="ir-x">×</div></div>
<div class="ir"><div class="ir-q">3 cloves</div><div class="ir-n">Garlic</div><div class="ir-x">×</div></div>
</div>
<button class="bg" style="width:100%;font-size:11px;padding:7px;margin-bottom:12px;">+ Add ingredient</button>
<div class="eye" style="margin-bottom:6px;">Tags (required)</div>
<div style="font-size:11px;color:var(--color-text-muted);margin-bottom:4px;">Effort</div>
<div style="display:flex;gap:4px;margin-bottom:10px;"><span class="tc s">Easy</span><span class="tc">Medium</span><span class="tc">Hard</span></div>
<div style="font-size:11px;color:var(--color-text-muted);margin-bottom:4px;">Category (pick ≥ 1)</div>
<div style="margin-bottom:14px;"><span class="tc">Chicken</span><span class="tc">Fish</span><span class="tc s">Vegetarian</span><span class="tc s">Child-friendly</span><span class="tc">Pasta</span></div>
<button class="bp">Save recipe</button>
</div>
</div>
</div>
</div>
<div class="prev-col">
<div class="bp-lbl">Desktop · 1040px</div>
<div class="desk" style="min-height:540px;">
<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><div class="dsb-nl">Plan</div><div class="dsb-ni"><span class="dsb-nc">📅</span>Planner</div><div class="dsb-ni a"><span class="dsb-nc">📖</span>Recipes</div><div class="dsb-ni"><span class="dsb-nc">🛒</span>Shopping</div></div></div>
</div>
<div class="dm">
<div class="dtb">
<div style="display:flex;align-items:center;gap:8px;"><span style="font-size:13px;color:var(--color-text-muted);">← Recipes</span><span style="color:var(--color-border);">/</span><span class="dtb-t">New recipe</span></div>
<div class="dtb-r"><button class="bg" style="padding:7px 14px;font-size:12px;">Cancel</button><button class="dab">Save recipe</button></div>
</div>
<div style="flex:1;display:flex;overflow:hidden;">
<!-- Left: form -->
<div style="flex:1;padding:24px;overflow-y:auto;border-right:1px solid var(--color-border);">
<div style="height:80px;background:var(--color-subtle);border:1px dashed var(--color-border);border-radius:var(--radius-lg);display:flex;align-items:center;justify-content:center;margin-bottom:20px;cursor:pointer;"><span style="font-size:13px;color:var(--color-text-muted);">📷 Add hero image</span></div>
<div class="fg"><label class="fl">Recipe name</label><input class="fi" value="Slow-roasted tomato pasta" style="font-size:16px;padding:12px 14px;"/></div>
<div style="display:flex;gap:12px;">
<div class="fg" style="flex:1;"><label class="fl">Serves</label><input class="fi" type="number" value="4"/></div>
<div class="fg" style="flex:1;"><label class="fl">Cook time</label><input class="fi" value="45 min"/></div>
<div class="fg" style="flex:1;"><label class="fl">Prep time</label><input class="fi" placeholder="Optional"/></div>
</div>
<div class="eye" style="margin:8px 0;">Ingredients</div>
<div style="border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:4px 14px;margin-bottom:8px;">
<div class="ir"><div class="ir-q">500g</div><div class="ir-n">Cherry tomatoes</div><div class="ir-x">×</div></div>
<div class="ir"><div class="ir-q">300g</div><div class="ir-n">Penne pasta</div><div class="ir-x">×</div></div>
<div class="ir"><div class="ir-q">3 cloves</div><div class="ir-n">Garlic</div><div class="ir-x">×</div></div>
<div class="ir"><div class="ir-q">2 tbsp</div><div class="ir-n">Olive oil</div><div class="ir-x">×</div></div>
</div>
<button class="bg" style="width:100%;font-size:12px;padding:8px;">+ Add ingredient</button>
<div class="eye" style="margin:16px 0 8px;">Steps</div>
<div style="display:flex;gap:10px;padding:8px 0;border-bottom:1px solid var(--color-subtle);"><div style="width:24px;height:24px;border-radius:50%;background:var(--color-subtle);display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:500;flex-shrink:0;">1</div><div style="font-size:13px;line-height:1.5;">Preheat oven to 180°C. Halve the tomatoes and place on a baking tray.</div></div>
<div style="display:flex;gap:10px;padding:8px 0;border-bottom:1px solid var(--color-subtle);"><div style="width:24px;height:24px;border-radius:50%;background:var(--color-subtle);display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:500;flex-shrink:0;">2</div><div style="font-size:13px;line-height:1.5;">Drizzle with olive oil, season. Roast for 40 minutes.</div></div>
<div style="display:flex;gap:10px;padding:8px 0;"><div style="width:24px;height:24px;border-radius:50%;background:var(--color-subtle);display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:500;flex-shrink:0;">3</div><div style="font-size:13px;line-height:1.5;">Cook pasta. Toss with roasted tomatoes and garlic.</div></div>
<button class="bg" style="width:100%;font-size:12px;padding:8px;margin-top:8px;">+ Add step</button>
</div>
<!-- Right: tags + preview -->
<div style="width:280px;flex-shrink:0;padding:24px;background:var(--color-surface);overflow-y:auto;">
<div class="eye" style="margin-bottom:8px;">Tags (required)</div>
<div style="font-size:11px;font-weight:500;margin-bottom:4px;">Effort level</div>
<div style="display:flex;gap:4px;margin-bottom:14px;"><span class="tc s">Easy</span><span class="tc">Medium</span><span class="tc">Hard</span></div>
<div style="font-size:11px;font-weight:500;margin-bottom:4px;">Category</div>
<div style="margin-bottom:20px;"><span class="tc">Chicken</span><span class="tc">Fish</span><span class="tc">Beef</span><span class="tc s">Vegetarian</span><span class="tc s">Child-friendly</span><span class="tc">Pasta</span></div>
<div style="border-top:1px solid var(--color-border);padding-top:16px;">
<div class="eye" style="margin-bottom:8px;">Live preview</div>
<div style="background:var(--color-page);border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;">
<div style="height:48px;background:var(--green-tint);display:flex;align-items:center;justify-content:center;font-size:18px;">🍝</div>
<div style="padding:10px;"><div style="font-family:var(--font-display);font-size:13px;margin-bottom:3px;">Slow-roasted tomato pasta</div><div style="font-size:9px;color:var(--color-text-muted);margin-bottom:4px;">45 min · 4 servings · 4 ingredients</div><div style="display:flex;gap:3px;"><span class="badge badge-g">Easy</span><span class="badge badge-g">Vegetarian</span><span class="badge badge-y">Child-friendly</span></div></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="agent">
<h4>B3 · Add/edit recipe</h4>
<pre>/* Desktop: 224px sidebar + topbar (breadcrumb + Save) + split content:
* Left (flex:1, page bg): hero upload + name + serves/time/prep (3-col) + ingredients + steps
* Right (280px, surface bg): effort chips + category chips + live preview card
* Form content is NOT in a card — it's directly on the page bg.
* Tags panel (right) uses surface bg as a section differentiator, not as a card.
* Mobile: single scroll, full width. Back + title + Save in topbar.
* Ingredient autocomplete: citext ILIKE from ingredient table. */</pre>
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
<tr class="grp"><td colspan="3">Desktop</td></tr>
<tr><td>Form area</td><td>flex:1, page bg, 24px padding, border-right</td><td>Contains: hero + name (16px input) + 3-col row + ingredients + steps</td></tr>
<tr><td>Tags panel</td><td>280px, surface bg, 24px padding</td><td>Effort chips + category chips + divider + live preview card</td></tr>
<tr><td>Live preview</td><td>Mini B1 card inside the panel</td><td>Updates as user types. Shows name, time, servings, ingredient count, tags.</td></tr>
</tbody></table>
</div>
</div>
<!-- ═══ LLM IMPLEMENTATION GUIDE ═══ -->
<div class="llm">
<h2>Implementation Guide — J1 Add a Recipe</h2>
<h3>1. Journey Flow</h3>
<p>B1 (Recipe library) → B3 (Add/edit form) → B1 (Recipe library). Actor: <strong>Planner only</strong>.</p>
<h3>2. Screen B1 — Recipe Library</h3>
<ul>
<li><strong>Mobile:</strong> topbar + 2-col card grid (8px gap, 64px image, no tags on cards).</li>
<li><strong>Desktop:</strong> 224px sidebar + topbar (search input 220px + Add button) + filter chips row + 4-col grid (12px gap, 100px image, tags visible as badge row).</li>
<li><strong>Filter chips:</strong> sourced from the <code>tag</code> table. Active chip = <code>green-tint</code> bg / <code>green-dark</code> text. Inactive chip = <code>border-default</code>. Style: <code>font-size:11px; font-weight:500; padding:5px 14px; border-radius:12px</code>.</li>
<li>Card click → B2 (recipe detail). Add button → B3 (empty form).</li>
</ul>
<h3>3. Screen B3 — Add/Edit Recipe</h3>
<ul>
<li>Single form component with two states: empty (new recipe) vs prefilled (edit existing recipe).</li>
<li><strong>Design rule:</strong> B3 add = B3 edit — build once with two initial states.</li>
<li><strong>Mobile:</strong> single scroll column, full width.</li>
<li><strong>Desktop:</strong> 224px sidebar + topbar (breadcrumb + Save/Cancel buttons) + split content: form left (<code>flex:1</code>, page bg, 24px padding) + tags panel right (280px, surface bg).</li>
</ul>
<p><strong>Form sections:</strong></p>
<ol>
<li>Hero image upload (optional) — dashed border placeholder, click to upload.</li>
<li>Recipe name (required) — 16px font on desktop.</li>
<li>Serves / Cook time / Prep time — 3-col row on desktop, 2-col on mobile (prep time hidden or below).</li>
<li>Ingredients — editable list with autocomplete. Each row: quantity + name + remove button.</li>
<li>Steps — numbered list, optional at save. Each step: circle number + text.</li>
</ol>
<p><strong>Tags (required):</strong></p>
<ul>
<li>Effort level: Easy / Medium / Hard — single-select chip group.</li>
<li>Category: at least 1 required — Chicken, Fish, Beef, Vegetarian, Pasta, etc. Multi-select chips.</li>
</ul>
<p><strong>Desktop right panel:</strong> live preview card (mini B1 card) updates as user types — shows name, time, servings, ingredient count, and selected tags.</p>
<p><strong>Ingredient autocomplete:</strong> <code>citext ILIKE</code> query against the <code>ingredient</code> table.</p>
<h3>4. Data Operations</h3>
<table>
<thead><tr><th>Screen</th><th>Operation</th><th>Tables</th></tr></thead>
<tbody>
<tr><td>B1</td><td><code>SELECT</code> recipes with tags</td><td><code>recipe</code> JOIN <code>recipe_tag</code> + <code>tag</code></td></tr>
<tr><td>B3</td><td><code>INSERT</code> / <code>UPDATE</code> recipe</td><td><code>recipe</code></td></tr>
<tr><td>B3</td><td><code>INSERT</code> / <code>DELETE</code> ingredients</td><td><code>recipe_ingredient</code></td></tr>
<tr><td>B3</td><td><code>INSERT</code> / <code>DELETE</code> steps</td><td><code>recipe_step</code></td></tr>
<tr><td>B3</td><td><code>INSERT</code> / <code>DELETE</code> tags</td><td><code>recipe_tag</code></td></tr>
</tbody>
</table>
<p><strong>Minimum to save:</strong> name + effort tag + at least 1 category tag.</p>
<h3>5. Design Constraints</h3>
<ul>
<li>Tags power the variety algorithm — they are not cosmetic. Ensure tags are always saved correctly.</li>
<li>If entered from a planner day slot, offer to assign the recipe to that day after save.</li>
<li>Steps are optional — recipes without steps can still be planned and cooked from the ingredient list only.</li>
<li>Form content is NOT in a card on desktop — it sits directly on the page background.</li>
<li>The tags panel uses <code>surface</code> bg as a section differentiator, not as a card wrapper.</li>
</ul>
<h3>6. Accessibility</h3>
<ul>
<li>All form inputs must have associated <code>&lt;label&gt;</code> elements.</li>
<li>Ingredient and step lists must support keyboard navigation (add, remove, reorder).</li>
<li>Chip selections (effort level, category tags) must use proper ARIA attributes: <code>role="radiogroup"</code> for single-select effort, <code>role="group"</code> with <code>aria-pressed</code> for multi-select categories.</li>
<li>Live preview region should use <code>aria-live="polite"</code> to announce updates.</li>
</ul>
</div>
</div>
</body>
</html>