Files
mealprep/specs/frontend/j4-adapt-on-the-fly.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

296 lines
26 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>J4 — Adapt on the Fly</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-light);border-radius:var(--radius-lg);padding:28px 24px;margin-top:64px;}
.llm h3{font-family:var(--font-display);font-size:18px;font-weight:500;letter-spacing:-.02em;color:var(--green-dark);margin-bottom:16px;}
.llm h4{font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--green-dark);margin-top:20px;margin-bottom:8px;}
.llm p,.llm li{font-size:12px;color:var(--color-text-muted);line-height:1.7;}
.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(--green-tint);color:var(--green-dark);padding:1px 5px;border-radius:3px;}
@media(max-width:900px){.doc{padding:24px 16px 80px;}}
</style>
</head>
<body>
<div class="doc">
<!-- ═══ J4 SWAP ═══ -->
<div class="jh jh-o">
<div class="jn">J4</div>
<div><h2>Adapt on the fly</h2><p>Mid-week swap in ≤ 3 taps. Action sheet → pick replacement → done.</p><div class="fl">C1 → action sheet → C2 swap → C1 · Easiest first</div></div>
</div>
<!-- ═══ SWAP TRIGGER ═══ -->
<div class="scr" id="swap-trigger">
<div class="scr-head"><h3>Swap trigger</h3><span class="scr-id">C1 overlay</span></div>
<div class="scr-desc">Mobile: tap meal → bottom action sheet (Swap / Cook / View / Cancel). Desktop: no action sheet needed — the C1 detail panel already has a "Swap meal" button. Clicking it transitions the detail panel content to show swap suggestions inline.</div>
<div class="scr-var"><strong>V4 · Action sheet</strong> (mobile) · Detail panel button (desktop)</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Mobile · Action sheet</div>
<div class="phone">
<div class="pst"><b>17:15</b><span>●●● WiFi 🔋</span></div>
<div class="pb">
<div class="mtb" style="opacity:.4;"><div class="mtb-t" style="font-size:16px;">This week</div></div>
<div style="padding:8px 12px;opacity:.4;">
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:10px;margin-bottom:6px;font-family:var(--font-display);font-size:12px;">Mon · Chicken stir-fry</div>
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:10px;font-family:var(--font-display);font-size:12px;">Tue · Tomato pasta</div>
</div>
<div style="flex:1;display:flex;flex-direction:column;justify-content:flex-end;">
<div style="background:var(--color-page);border-top:1px solid var(--color-border);border-radius:var(--radius-xl) var(--radius-xl) 0 0;padding:16px;box-shadow:0 -4px 20px rgba(0,0,0,.12);">
<div style="width:32px;height:4px;border-radius:2px;background:var(--color-border);margin:0 auto 12px;"></div>
<div style="font-family:var(--font-display);font-size:15px;font-weight:500;margin-bottom:4px;">Tuesday — Tomato pasta</div>
<div style="font-size:11px;color:var(--color-text-muted);margin-bottom:12px;">45 min · Easy · Vegetarian</div>
<div style="display:flex;flex-direction:column;gap:6px;">
<div style="padding:12px;border-radius:var(--radius-lg);background:var(--orange-tint);border:1px solid #FBCDA4;font-size:13px;font-weight:500;color:var(--orange-dark);text-align:center;">↻ Swap this meal</div>
<div style="padding:12px;border-radius:var(--radius-lg);background:var(--green-tint);border:1px solid var(--green-light);font-size:13px;font-weight:500;color:var(--green-dark);text-align:center;">🍳 Cook now</div>
<div style="padding:12px;border-radius:var(--radius-lg);background:var(--color-subtle);border:1px solid var(--color-border);font-size:13px;font-weight:500;color:var(--color-text-muted);text-align:center;">👁 View recipe</div>
<div style="padding:12px;font-size:13px;color:var(--color-text-muted);text-align:center;">Cancel</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="agent">
<h4>Swap trigger</h4>
<pre>/* Mobile: tap meal card → bottom action sheet. Planner dims to 40%.
* Sheet: drag handle + meal name + meta + 4 action buttons stacked.
* Swap = orange-tint. Cook = green-tint. View = subtle. Cancel = no bg.
* Desktop: no action sheet. C1 detail panel has "Swap meal" ghost button (per planner-spec).
* Clicking "Swap meal" transitions the detail panel to show swap suggestions inline.
* Tap count: Mobile 3 (card → Swap → Pick). Desktop 2 (Swap → Pick). */</pre>
</div>
</div>
<!-- ═══ C2 SWAP CONTEXT ═══ -->
<div class="scr" id="swap-context">
<div class="scr-head"><h3>C2 in swap context</h3><span class="scr-id">C2 swap</span></div>
<div class="scr-desc">Mobile: bottom sheet over C1 with "Replacing" banner + easiest-first suggestions. Desktop: swap suggestions render inline in the C1 detail panel (replacing the meal detail) — the calendar grid stays visible alongside. No page navigation needed.</div>
<div class="scr-var"><strong>V1 · Quick swap sheet</strong> (mobile) · Inline detail panel (desktop)</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Mobile · Swap sheet</div>
<div class="phone">
<div class="pst"><b>17:16</b><span>●●● WiFi 🔋</span></div>
<div class="pb">
<div style="opacity:.3;padding:10px 16px;border-bottom:1px solid var(--color-border);font-family:var(--font-display);font-size:16px;font-weight:500;">This week</div>
<div style="flex:1;display:flex;flex-direction:column;justify-content:flex-end;">
<div style="background:var(--color-page);border-top:1px solid var(--color-border);border-radius:var(--radius-xl) var(--radius-xl) 0 0;padding:16px;box-shadow:0 -4px 20px rgba(0,0,0,.12);">
<div style="width:32px;height:4px;border-radius:2px;background:var(--color-border);margin:0 auto 10px;"></div>
<div style="background:var(--orange-tint);border:1px solid #FBCDA4;border-radius:var(--radius-lg);padding:10px 12px;margin-bottom:14px;">
<div class="eye" style="color:var(--orange-dark);margin-bottom:4px;">Replacing Tuesday's meal</div>
<div style="font-family:var(--font-display);font-size:14px;text-decoration:line-through;opacity:.6;">Tomato pasta · 45 min · Easy</div>
</div>
<div class="eye" style="margin-bottom:6px;">Swap to (easiest first)</div>
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:10px 12px;margin-bottom:6px;display:flex;align-items:center;gap:8px;">
<div style="flex:1;"><div style="font-family:var(--font-display);font-size:13px;">Quick carbonara</div><div style="font-size:9px;color:var(--color-text-muted);">20 min · Easy · Pasta</div></div>
<div style="font-size:11px;color:var(--green);font-weight:500;">Pick</div>
</div>
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:10px 12px;margin-bottom:6px;display:flex;align-items:center;gap:8px;">
<div style="flex:1;"><div style="font-family:var(--font-display);font-size:13px;">Chicken stir-fry</div><div style="font-size:9px;color:var(--color-text-muted);">25 min · Easy</div><div style="font-size:9px;color:var(--yellow-text);">⚠ Already on Mon</div></div>
<div style="font-size:11px;color:var(--green);font-weight:500;">Pick</div>
</div>
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:10px 12px;margin-bottom:6px;display:flex;align-items:center;gap:8px;">
<div style="flex:1;"><div style="font-family:var(--font-display);font-size:13px;">Mushroom risotto</div><div style="font-size:9px;color:var(--color-text-muted);">50 min · Medium · Veggie</div></div>
<div style="font-size:11px;color:var(--green);font-weight:500;">Pick</div>
</div>
<div style="text-align:center;padding:8px 0;font-size:11px;color:var(--color-text-muted);">Cancel</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="agent">
<h4>C2 swap context</h4>
<pre>/* Mobile: bottom sheet over dimmed C1. "Replacing" banner + suggestion list.
* Sorted EASIEST FIRST (effort ASC, cook_time ASC) — different from J2.
* "Pick" → UPDATE week_plan_slot. Dismiss sheet. No confirmation dialog (undo toast instead).
* Desktop: detail panel (280px) transitions in-place. Calendar grid stays visible.
* Replacing header: orange-tint, old meal struck through.
* Suggestion cards: compact, fitting panel width. Name + meta + "Pick" link.
* Tap count: Mobile 3. Desktop 2 (faster — no action sheet intermediary). */</pre>
</div>
</div>
<!-- ═══ LLM INSTRUCTIONS ═══ -->
<div class="llm">
<h3>LLM Implementation Instructions — J4 Adapt on the Fly</h3>
<h4>1. Journey Flow</h4>
<p>C1 → action sheet (mobile) or detail panel button (desktop) → swap suggestions → pick → C1.
Actor: <strong>Planner</strong>. Frequency: 1-2x/week. Urgency: <strong>HIGH</strong>.</p>
<h4>2. Constraint: 3 taps maximum</h4>
<p>From "Swap" to updated plan in no more than 3 taps.</p>
<ul>
<li><strong>Mobile: 3 taps</strong> — card → Swap → Pick</li>
<li><strong>Desktop: 2 taps</strong> — Swap → Pick (no action sheet intermediary)</li>
</ul>
<h4>3. Mobile: Action Sheet</h4>
<ul>
<li>Bottom sheet pulls up on meal tap; background dims to <strong>40% opacity</strong>.</li>
<li>Drag handle: 32px wide, 4px height, <code>var(--color-border)</code> background.</li>
<li>Meal title in 15px display font + metadata in 11px muted text.</li>
<li>4 stacked buttons:
<ol>
<li><strong>"Swap this meal"</strong><code>orange-tint</code> bg / <code>orange-dark</code> text</li>
<li><strong>"Cook now"</strong><code>green-tint</code> bg / <code>green-dark</code> text</li>
<li><strong>"View recipe"</strong><code>subtle</code> bg / <code>muted</code> text</li>
<li><strong>"Cancel"</strong> — no background, muted text</li>
</ol>
</li>
</ul>
<h4>4. Mobile: Swap Suggestions (Bottom Sheet)</h4>
<ul>
<li>"Replacing" banner: <code>orange-tint</code> background, old meal name struck through.</li>
<li>"Swap to (easiest first)" eyebrow label above suggestion list.</li>
<li>Compact suggestion cards: recipe name + time/effort/tag + "Pick" link on the right.</li>
<li><strong>Sorted EASIEST FIRST</strong><code>effort ASC, cook_time ASC</code>. This is DIFFERENT from C2 in J2, which sorts by variety score.</li>
<li>"Pick" action: <code>UPDATE week_plan_slot</code> with new <code>recipe_id</code> → dismiss sheet → show <strong>undo toast</strong> (NOT a confirmation dialog).</li>
</ul>
<h4>5. Desktop: Inline Panel</h4>
<ul>
<li>No action sheet needed — the C1 detail panel (280px wide) already has a "Swap meal" ghost button.</li>
<li>Clicking the button transitions the detail panel content <strong>in-place</strong> to show swap suggestions.</li>
<li>Calendar grid stays visible alongside the panel at all times.</li>
<li>Same sorting (<code>effort ASC, cook_time ASC</code>) and "Replacing" header as mobile.</li>
</ul>
<h4>6. Why Easiest First</h4>
<p>Mid-week swaps typically happen because the original plan was too ambitious. Sorting by effort makes the fastest, lowest-effort options most visible, matching the user's intent to simplify.</p>
<h4>7. Data Operations</h4>
<ul>
<li><strong>Writes:</strong> <code>week_plan_slot UPDATE</code> — sets new <code>recipe_id</code> on the slot.</li>
<li>Swap is logged: both the original meal (marked as not cooked) and the replacement are recorded.</li>
<li>Original uncooked meal remains in the recipe library for future weeks.</li>
<li>Variety score recalculates immediately after swap.</li>
</ul>
<h4>8. Design Constraints</h4>
<ul>
<li><strong>Speed over deliberation</strong> — undo toast instead of confirmation dialog. The user is in a hurry mid-week.</li>
<li>Orange accent color for swap context: <code>orange-tint</code> background, <code>orange-dark</code> text on banners and primary action.</li>
<li>Variety filter still applies to suggestions (duplicates get a warning), just sorted differently than J2.</li>
</ul>
</div>
</div>
</body>
</html>