Your week
Tap an empty slot to choose a meal. Tap a filled one to read the recipe. Every change saves to the server as you make it, so the week you plan here is the week your phone shows.
Rotation
How many different meals do you want in each slot this week? A low number means less shopping and less thinking, and more repetition — two breakfasts across seven days means eating each one three or four times. Because a short rotation multiplies whatever it contains, choosing well matters more as the number falls. So the planner takes the most protein-dense meals available, subject to two rules: no protein source may fill more than half a rotation, which at two meals forces two different sources; and the week must hold at least two oily-fish servings for omega-3, which protein density alone would never choose, because oily fish costs more calories per gram of protein than cod does.
You can also name the meals yourself. A choice is a pin, not a filter: pick two of five dinners and the planner still fills the other three by protein density, ranking around your choices rather than over them. The limit is the rotation number itself — there is only one idea of how many meals a slot holds, so the chooser stops at it. A meal you pinned is never displaced, not by the half-rotation cap and not by the omega-3 rule; the report tells you when your choice has overruled one of them, because a rule that bends silently is a rule you cannot trust.
Saved weeks
Every time you build a week it is saved here, dated. Editing a slot changes the week you are living in; building a new one starts a new record. Restoring copies an old week forward rather than reopening it — history you can edit is not history, and the point of an archive is to tell you what you actually ate.
Shopping list
A snapshot of the week you built it from, not a live view of it — so it cannot rewrite itself while you are standing in the shop. Grouped by aisle. Tick what you already have and the ticks persist. Dinner quantities feed four; breakfast and lunch feed you alone. Anything you buy as a whole object — a tin, a lemon, a clove — is rounded up, with the shortfall shown beside it. Things you weigh are not.
Weight
One weigh-in per person per week — the same unit everything here plans in. Each entry is dated, and that date ties it to the meal plan in force that day: every build and restore on the Week tab is a dated record, so the plan behind a good or bad fortnight is named on the row, and its meal names open their recipes. Logging twice in a week overwrites — a second weigh-in is a correction, not a second data point. Losses read green because losing is what this plan is for.
Recipes
… meals, ranked by the share of their calories that come from protein. Tap any card to read it. Breakfast and lunch serve one; dinners serve four and carry a note on splitting them for the children.
Ingredients
Your opinions about food, as data. This is a weighting layer over the … recipes, not a separate list — a stance changes how the planner scores every meal containing that ingredient, and Banned removes those meals from the planner and the picker entirely. A meal's adjustments are summed and capped at ±20 points, because preference should tilt the ranking, not overturn it: protein density runs 21 to 63 across the library, and four liked ingredients should not outrank cod on sentiment alone.
Most stances live in ingredients.js and arrive with each deploy. But you can also analyse a new ingredient right here: name a food, the model looks up its numbers and argues about whether it earns a place, and if you agree it joins the library — stored in the database, so it survives deploys without a git commit. It can then suggest recipes the new food would improve, and new ones built around it. Changing a stance below is a local experiment, marked as such, and reversible.
Within each stance the best food comes first, and “best” is computed rather than chosen: protein density leads, so cod heads Loved and quark follows it. But a food carrying under 5g of protein per 100g is not a protein source and does not compete on that scale — those sort by fewest calories per 100g, because once a food gives you no protein the only benefit left is how little it costs. Asparagus therefore sits above avocado, and avocado above olive oil.
The tags beside each name are derived from the numbers, never typed. Tier 1 means 55% or more of the food's calories arrive as protein, Tier 2 is 30 to 55%, Tier 3 is below 30 — an accent, not a base. A food is only tiered once it carries at least 5g of protein per 100g; below that it reads Trace protein, because a mushroom computes to 55% and is not cod. Eat freely is under 40 kcal per 100g and Calorie-dense is 400 or more, because calorie density is a property of the food. Carb-heavy is measured differently — 20g of net carbs per 100g for things you weigh, but 15g per item for things you count, because carbohydrate is spent from a daily budget one serving at a time. A crispbread is 60g of carbs per 100g and 6g per slice, and only the second number is one you ever eat. Only Omega-3, High fibre, High sodium and Ultra-processed are declared by hand, because no arithmetic on protein, carbs and calories can tell you a sardine has omega-3.