{
  "spec": "quick-html/v0",
  "id": "meal-plan",
  "version": "0.1.0",
  "name": "Meal Plan",
  "description": "Week-at-a-glance meal plan for the fridge door: seven equal day columns crossed with breakfast, lunch, dinner and snack, plus a tickable shopping list built from those meals and an optional nutrition-target strip. Pick it when a household plans a week of cooking.",
  "useWhen": [
    "meal plan",
    "weekly meal plan",
    "plan the week's dinners",
    "what are we eating this week",
    "family menu for the week",
    "meal prep planner",
    "shopping list from a menu",
    "weekmenu",
    "weekplanning eten",
    "wochenplan essen",
    "menu de la semaine"
  ],
  "format": "printable",
  "theme": "health-sport",
  "tags": [
    "meals",
    "weekly",
    "nutrition",
    "shopping-list",
    "kitchen"
  ],
  "language": "en",
  "page": {
    "size": "A4",
    "orientation": "landscape"
  },
  "modules": [
    "edit-overlay",
    "print-helper"
  ],
  "slots": [
    {
      "name": "doc-title",
      "type": "text",
      "required": true,
      "example": "Weekmenu — week 36, huishouden Wolters",
      "notes": "Browser tab title and download filename; mirror the week label and the household."
    },
    {
      "name": "plan-title",
      "type": "text",
      "required": true,
      "example": "Weekmenu",
      "notes": "Big display word at top left. One or two words — 'Weekmenu', 'Meal plan', 'Wochenplan'. Under ~18 chars."
    },
    {
      "name": "household-line",
      "type": "text",
      "required": true,
      "example": "Marit & Pieter Wolters · Ties (9) & Fenna (6) · Deventer",
      "notes": "Who eats from this sheet, separated by ' · '. Ages in brackets help portioning. Under ~70 chars."
    },
    {
      "name": "week-label",
      "type": "text",
      "required": true,
      "example": "Week 36 · 31 Aug – 6 Sep 2026",
      "notes": "ISO week number then the Monday–Sunday span: 'Week 36 · 31 Aug – 6 Sep 2026'. Check the week number against a real calendar."
    },
    {
      "name": "meta-note",
      "type": "text",
      "required": false,
      "example": "Shop Saturday morning · Meat-free Mon & Wed · Fenna swims Thu 17.30",
      "notes": "The rules this week was planned around: shopping day, meat-free days, evening commitments. Under ~80 chars or it wraps, ' · ' separated."
    },
    {
      "name": "row-breakfast",
      "type": "text",
      "required": true,
      "example": "Breakfast",
      "notes": "Left-gutter label for row 1. One word — translate it ('Ontbijt', 'Frühstück') to match the meal text."
    },
    {
      "name": "row-lunch",
      "type": "text",
      "required": true,
      "example": "Lunch",
      "notes": "Left-gutter label for row 2. One word."
    },
    {
      "name": "row-dinner",
      "type": "text",
      "required": true,
      "example": "Dinner",
      "notes": "Left-gutter label for row 3. One word ('Avondeten', 'Abendessen')."
    },
    {
      "name": "row-snack",
      "type": "text",
      "required": true,
      "example": "Snack",
      "notes": "Left-gutter label for row 4. One word. Keep the row even when nobody plans snacks — fill the cells with '—'."
    },
    {
      "name": "shopping-note",
      "type": "text",
      "required": false,
      "example": "Zaterdagmarkt + supermarkt, Sat 09.00 · staples assumed in stock · budget € 135",
      "notes": "Where and when the shopping happens, plus any assumption the list relies on. Under ~90 chars."
    },
    {
      "name": "target-kcal",
      "type": "text",
      "required": false,
      "example": "2.100 kcal",
      "notes": "Daily energy target with unit; European decimals ('2.100 kcal'). Lives in the targets strip."
    },
    {
      "name": "target-protein",
      "type": "text",
      "required": false,
      "example": "110 g",
      "notes": "Daily protein target with unit. Lives in the targets strip."
    },
    {
      "name": "target-veg",
      "type": "text",
      "required": false,
      "example": "400 g + 2 st",
      "notes": "Daily vegetable/fruit target. Short — the cell is 128px wide. Lives in the targets strip."
    },
    {
      "name": "target-note",
      "type": "text",
      "required": false,
      "example": "Pieter is five weeks out from the Rotterdam marathon — carb-heavy Saturday and Sunday, a protein source at every dinner.",
      "notes": "One sentence on why these numbers, e.g. a training block. Under ~130 chars. Lives in the targets strip."
    },
    {
      "name": "notes-text",
      "type": "html",
      "required": false,
      "example": "<ul><li>Wednesday: double pan of pompoen-linzencurry — half for Thursday.</li><li>Snert is better on day two — hold two portions back for Monday.</li></ul>",
      "notes": "Batch-cooking, freezing and leftovers notes as a <ul> of 2–4 <li> (they print in three columns, so keep each under ~70 chars to stay on one line) or one <p>."
    },
    {
      "name": "footer-line",
      "type": "text",
      "required": false,
      "example": "Wolters household · Deventer · planned Sat 29 Aug 2026",
      "notes": "Bottom-left attribution: household, place, when the plan was made."
    }
  ],
  "repeats": [
    {
      "name": "day",
      "description": "One whole day column of the week grid: the day header plus that day's four meals. Add one block per day, Monday first, at most seven — columns six and seven are automatically tinted and coloured as the weekend.",
      "min": 1,
      "max": 7,
      "slots": [
        {
          "name": "day-label",
          "type": "text",
          "required": true,
          "example": "Monday",
          "notes": "Full weekday name; printed in caps by the template. Localise it ('Maandag', 'Montag') to match the meal text."
        },
        {
          "name": "day-date",
          "type": "date",
          "required": true,
          "example": "31 Aug",
          "notes": "Day and abbreviated month, no year: '31 Aug', '1 Sep'. Must follow on from the previous column."
        },
        {
          "name": "day-breakfast",
          "type": "html",
          "required": true,
          "example": "Havermout met appel<span>kaneel · walnoot</span>",
          "notes": "Dish name under ~40 chars, then an optional detail in a <span> (under ~24 chars) that prints small and grey. '—' if not planned."
        },
        {
          "name": "day-lunch",
          "type": "html",
          "required": true,
          "example": "Boterhammen oude kaas<span>+ komkommer, tomaat</span>",
          "notes": "Same shape as day-breakfast. Good place for a leftovers lunch: 'Restjes pasta van maandag'."
        },
        {
          "name": "day-dinner",
          "type": "html",
          "required": true,
          "example": "Courgette-ricottapasta<span>20 min · vega</span>",
          "notes": "Same shape. Put the useful constraint in the <span>: '20 min', 'vega', 'dubbele pan', 'voor zwemles'."
        },
        {
          "name": "day-snack",
          "type": "html",
          "required": true,
          "example": "Appels & amandelen<span>in de broodtrommel</span>",
          "notes": "Same shape. Write '—' in every day when the household does not plan snacks; never delete the row."
        }
      ]
    },
    {
      "name": "shopping-item",
      "description": "One line of the shopping list: a tick box, the item and its quantity. A section header (Produce, Bakery, Dairy & eggs, Fish & meat, Pantry, Frozen — supermarket walking order) is the same block with item-qty left empty and item-name set to only '<strong>Produce</strong>' — the tick box then disappears automatically.",
      "min": 1,
      "max": 40,
      "slots": [
        {
          "name": "item-name",
          "type": "html",
          "required": true,
          "example": "Courgettes",
          "notes": "Item as it is bought, under ~26 chars or it is clipped. For a section header use only '<strong>Produce</strong>' and no other <strong> anywhere."
        },
        {
          "name": "item-qty",
          "type": "text",
          "required": true,
          "example": "2 st",
          "notes": "Merged metric quantity: '500 g', '1,2 kg', '2 st', '4 × 150 g', '2 × 400 ml'. Leave completely empty on a section-header line."
        }
      ]
    }
  ],
  "optionals": [
    {
      "name": "targets",
      "description": "Nutrition-target strip under the header (energy, protein, vegetables plus one line of context) for households training towards something."
    },
    {
      "name": "notes",
      "description": "Batch-cooking, freezing and leftovers notes in three columns below the shopping list."
    }
  ],
  "changelog": [
    {
      "version": "0.1.0",
      "date": "2026-08-19",
      "notes": "Initial release: A4 landscape week-at-a-glance meal grid (7 day columns × 4 meal rows with alternating row bands and tinted weekend), derived tick-box shopping list in four columns, optional nutrition targets and batch-cooking notes."
    }
  ],
  "agentInstructions": "Delete every data-example element first. Add ONE 'day' block per planned day, Monday first, MAXIMUM 7; columns 6 and 7 are styled as the weekend automatically. day-date runs on ('31 Aug', '1 Sep'). Each meal is a dish name UNDER ~40 CHARS plus an optional short detail in a <span> — longer names break the seven equal columns. Never invent food: if the user gives only dinners, leave breakfast, lunch and snack as '—'. Mark a reheat night explicitly, e.g. 'Leftovers — Wednesday's curry'. THEN DERIVE THE SHOPPING LIST FROM THE MEALS ACTUALLY PLANNED — this is the real work, do it slowly: walk every dish, write down each ingredient it needs, MERGE duplicates into one line with the quantities added up (one 'Eieren 18 st', not three egg lines), then sort the lines by supermarket section and open each section with a header block (item-qty empty, item-name only '<strong>Produce</strong>'). Quantities metric: '500 g', '1,2 kg', '2 st', '2 × 400 ml'. Honour every stated dietary rule (meat-free days, allergies, halal) and never claim a dish is free of an allergen. Drop the targets block unless the user gave nutrition numbers, and the notes block if there is nothing real to say. Keep unknown household details as [FAMILY NAME] or [CITY] and tell the user to correct them with the Edit button.",
  "license": "MIT",
  "authors": [
    {
      "github": "bvrln"
    }
  ]
}
