// pantry-recipes-ext.jsx — extended recipe catalog + TheMealDB online browser
//
// Two sources, both bilingual (FR/EN):
//   1. EXTRA_RECIPES — a curated, generated catalog stored in-app. Tagged by
//      diet (vegetarian/vegan/etc.) and allergens so the Recipes page can
//      filter by régime / allergies / "sans X".
//   2. window.MealDB — a thin client over TheMealDB (free, no key) that returns
//      recipes with REAL PHOTOS. Used by the "Explore" tab; degrades silently
//      offline.
//
// Recipe shape produced here matches what RecipeFeedItem / RecipeDetail expect:
//   { id, name:{fr,en}, image?, hero, bg1, bg2, minutes, portions,
//     difficulty:{fr,en}, tagline:{fr,en}, mealType, diet:[], allergens:[],
//     uses:[], ingredients:[{name:{fr,en}, emoji, qty:{fr,en}, missing}],
//     steps:[{fr,en}], nutrition? }

// ── Diet / allergen vocab (stable keys; labels live in i18n) ──
const DIETS = ['vegetarian', 'vegan', 'pescatarian', 'glutenFree', 'dairyFree', 'lowCarb', 'highProtein'];
const ALLERGENS = ['gluten', 'dairy', 'eggs', 'nuts', 'peanuts', 'soy', 'shellfish', 'fish', 'sesame'];

// ── Curated generated catalog ───────────────────────────────────
// Compact but varied; covers omnivore / veg / vegan / GF / DF / high-protein /
// low-carb across breakfast/lunch/dinner. No external image (emoji + gradient)
// unless an `image` URL is provided.
function mkRecipe(o) {
  return {
    custom: false, external: false,
    bg1: o.bg1 || '#A8C895', bg2: o.bg2 || '#7FA86A',
    portions: o.portions || 2,
    difficulty: o.difficulty || { fr: 'Facile', en: 'Easy' },
    tagline: o.tagline || { fr: '', en: '' },
    diet: o.diet || [], allergens: o.allergens || [],
    uses: o.uses || [],
    ingredients: o.ingredients || [],
    steps: o.steps || [],
    nutrition: o.nutrition || null,
    ...o,
  };
}

const EXTRA_RECIPES = [
  mkRecipe({
    id: 'x_overnight_oats', mealType: 'breakfast', minutes: 5, hero: '🥣',
    bg1: '#E8D9B5', bg2: '#D9BE7E',
    name: { fr: 'Overnight oats aux petits fruits', en: 'Berry overnight oats' },
    tagline: { fr: 'Prépare la veille, aucun feu', en: 'Make-ahead, no cooking' },
    diet: ['vegetarian'], allergens: ['gluten'],
    ingredients: [
      { name: { fr: 'Flocons d\'avoine', en: 'Rolled oats' }, emoji: '🌾', qty: { fr: '½ tasse', en: '½ cup' } },
      { name: { fr: 'Lait', en: 'Milk' }, emoji: '🥛', qty: { fr: '½ tasse', en: '½ cup' } },
      { name: { fr: 'Petits fruits', en: 'Mixed berries' }, emoji: '🫐', qty: { fr: '½ tasse', en: '½ cup' } },
    ],
    steps: [
      { fr: 'Mélange avoine et lait dans un pot.', en: 'Combine oats and milk in a jar.' },
      { fr: 'Ajoute les fruits, couvre, réfrigère une nuit.', en: 'Add berries, cover, chill overnight.' },
    ],
    nutrition: { cal: 320, p: 12, c: 52, f: 7 },
  }),
  mkRecipe({
    id: 'x_tofu_scramble', mealType: 'breakfast', minutes: 15, hero: '🍳',
    bg1: '#F0E0A8', bg2: '#E8C95C',
    name: { fr: 'Brouillade de tofu', en: 'Tofu scramble' },
    tagline: { fr: 'Vegan, riche en protéines', en: 'Vegan, protein-packed' },
    diet: ['vegan', 'vegetarian', 'dairyFree', 'glutenFree'], allergens: ['soy'],
    ingredients: [
      { name: { fr: 'Tofu ferme', en: 'Firm tofu' }, emoji: '🧈', qty: { fr: '200 g', en: '200 g' } },
      { name: { fr: 'Curcuma', en: 'Turmeric' }, emoji: '🟡', qty: { fr: '½ c. à thé', en: '½ tsp' } },
      { name: { fr: 'Épinards', en: 'Spinach' }, emoji: '🥬', qty: { fr: '1 poignée', en: '1 handful' } },
    ],
    steps: [
      { fr: 'Émiette le tofu dans une poêle chaude.', en: 'Crumble tofu into a hot pan.' },
      { fr: 'Ajoute curcuma, sel; cuis 5 min.', en: 'Add turmeric, salt; cook 5 min.' },
      { fr: 'Incorpore les épinards jusqu\'à flétrir.', en: 'Stir in spinach until wilted.' },
    ],
    nutrition: { cal: 250, p: 20, c: 8, f: 16 },
  }),
  mkRecipe({
    id: 'x_greek_salad', mealType: 'lunch', minutes: 10, hero: '🥗',
    bg1: '#B8D89A', bg2: '#7FA86A',
    name: { fr: 'Salade grecque', en: 'Greek salad' },
    tagline: { fr: 'Fraîche, sans cuisson', en: 'Fresh, no-cook' },
    diet: ['vegetarian', 'glutenFree', 'lowCarb'], allergens: ['dairy'],
    ingredients: [
      { name: { fr: 'Concombre', en: 'Cucumber' }, emoji: '🥒', qty: { fr: '1', en: '1' } },
      { name: { fr: 'Tomates', en: 'Tomatoes' }, emoji: '🍅', qty: { fr: '2', en: '2' } },
      { name: { fr: 'Féta', en: 'Feta' }, emoji: '🧀', qty: { fr: '100 g', en: '100 g' } },
      { name: { fr: 'Olives', en: 'Olives' }, emoji: '🫒', qty: { fr: '½ tasse', en: '½ cup' } },
    ],
    steps: [
      { fr: 'Coupe légumes et féta en cubes.', en: 'Dice vegetables and feta.' },
      { fr: 'Mélange avec olives, huile, origan.', en: 'Toss with olives, oil, oregano.' },
    ],
    nutrition: { cal: 280, p: 9, c: 12, f: 22 },
  }),
  mkRecipe({
    id: 'x_lentil_soup', mealType: 'dinner', minutes: 30, hero: '🍲',
    bg1: '#E2B07A', bg2: '#C2884A',
    name: { fr: 'Soupe aux lentilles', en: 'Lentil soup' },
    tagline: { fr: 'Réconfortante, vegan', en: 'Hearty, vegan' },
    diet: ['vegan', 'vegetarian', 'dairyFree', 'glutenFree', 'highProtein'], allergens: [],
    ingredients: [
      { name: { fr: 'Lentilles', en: 'Lentils' }, emoji: '🟤', qty: { fr: '1 tasse', en: '1 cup' } },
      { name: { fr: 'Carottes', en: 'Carrots' }, emoji: '🥕', qty: { fr: '2', en: '2' } },
      { name: { fr: 'Oignon', en: 'Onion' }, emoji: '🧅', qty: { fr: '1', en: '1' } },
    ],
    steps: [
      { fr: 'Fais revenir oignon et carottes.', en: 'Sauté onion and carrots.' },
      { fr: 'Ajoute lentilles et bouillon; mijote 25 min.', en: 'Add lentils and broth; simmer 25 min.' },
    ],
    nutrition: { cal: 300, p: 18, c: 45, f: 4 },
  }),
  mkRecipe({
    id: 'x_salmon_bowl', mealType: 'dinner', minutes: 25, hero: '🐟',
    bg1: '#F0B49A', bg2: '#E08A77',
    name: { fr: 'Bol de saumon teriyaki', en: 'Teriyaki salmon bowl' },
    tagline: { fr: 'Riche en oméga-3', en: 'Omega-3 rich' },
    diet: ['pescatarian', 'dairyFree', 'highProtein'], allergens: ['fish', 'soy', 'sesame'],
    ingredients: [
      { name: { fr: 'Saumon', en: 'Salmon' }, emoji: '🐟', qty: { fr: '2 filets', en: '2 fillets' } },
      { name: { fr: 'Riz', en: 'Rice' }, emoji: '🍚', qty: { fr: '1 tasse', en: '1 cup' } },
      { name: { fr: 'Edamame', en: 'Edamame' }, emoji: '🫛', qty: { fr: '½ tasse', en: '½ cup' } },
    ],
    steps: [
      { fr: 'Cuis le riz.', en: 'Cook the rice.' },
      { fr: 'Poêle le saumon, glace au teriyaki.', en: 'Sear salmon, glaze with teriyaki.' },
      { fr: 'Assemble le bol avec edamame.', en: 'Assemble bowl with edamame.' },
    ],
    nutrition: { cal: 520, p: 38, c: 48, f: 18 },
  }),
  mkRecipe({
    id: 'x_chicken_stirfry', mealType: 'dinner', minutes: 20, hero: '🍗',
    bg1: '#E8C08A', bg2: '#D89A4A',
    name: { fr: 'Sauté de poulet aux légumes', en: 'Chicken veggie stir-fry' },
    tagline: { fr: 'Vide-frigo express', en: 'Quick fridge-clearer' },
    diet: ['dairyFree', 'highProtein'], allergens: ['soy'],
    ingredients: [
      { name: { fr: 'Poulet', en: 'Chicken' }, emoji: '🍗', qty: { fr: '400 g', en: '400 g' } },
      { name: { fr: 'Poivron', en: 'Bell pepper' }, emoji: '🫑', qty: { fr: '1', en: '1' } },
      { name: { fr: 'Brocoli', en: 'Broccoli' }, emoji: '🥦', qty: { fr: '1 tasse', en: '1 cup' } },
    ],
    steps: [
      { fr: 'Coupe et saisis le poulet.', en: 'Cut and sear the chicken.' },
      { fr: 'Ajoute les légumes, sauté 6 min.', en: 'Add veggies, stir-fry 6 min.' },
      { fr: 'Sauce soya, sers chaud.', en: 'Soy sauce, serve hot.' },
    ],
    nutrition: { cal: 420, p: 40, c: 18, f: 18 },
  }),
  mkRecipe({
    id: 'x_chickpea_curry', mealType: 'dinner', minutes: 25, hero: '🍛',
    bg1: '#E8C77A', bg2: '#D8A24A',
    name: { fr: 'Cari de pois chiches', en: 'Chickpea curry' },
    tagline: { fr: 'Vegan, épicé à ton goût', en: 'Vegan, spice to taste' },
    diet: ['vegan', 'vegetarian', 'dairyFree', 'glutenFree', 'highProtein'], allergens: [],
    ingredients: [
      { name: { fr: 'Pois chiches', en: 'Chickpeas' }, emoji: '🫛', qty: { fr: '1 boîte', en: '1 can' } },
      { name: { fr: 'Lait de coco', en: 'Coconut milk' }, emoji: '🥥', qty: { fr: '1 boîte', en: '1 can' } },
      { name: { fr: 'Tomates', en: 'Tomatoes' }, emoji: '🍅', qty: { fr: '2', en: '2' } },
    ],
    steps: [
      { fr: 'Fais revenir oignon, ail, épices à cari.', en: 'Sauté onion, garlic, curry spices.' },
      { fr: 'Ajoute pois chiches, tomates, lait de coco.', en: 'Add chickpeas, tomatoes, coconut milk.' },
      { fr: 'Mijote 15 min.', en: 'Simmer 15 min.' },
    ],
    nutrition: { cal: 410, p: 14, c: 44, f: 20 },
  }),
  mkRecipe({
    id: 'x_egg_fried_rice', mealType: 'lunch', minutes: 15, hero: '🍚',
    bg1: '#F0E0A8', bg2: '#E8C95C',
    name: { fr: 'Riz frit aux œufs', en: 'Egg fried rice' },
    tagline: { fr: 'Parfait pour le riz de la veille', en: 'Great for day-old rice' },
    diet: ['vegetarian', 'dairyFree'], allergens: ['eggs', 'soy'],
    ingredients: [
      { name: { fr: 'Riz cuit', en: 'Cooked rice' }, emoji: '🍚', qty: { fr: '2 tasses', en: '2 cups' } },
      { name: { fr: 'Œufs', en: 'Eggs' }, emoji: '🥚', qty: { fr: '2', en: '2' } },
      { name: { fr: 'Petits pois', en: 'Peas' }, emoji: '🟢', qty: { fr: '½ tasse', en: '½ cup' } },
    ],
    steps: [
      { fr: 'Brouille les œufs, réserve.', en: 'Scramble eggs, set aside.' },
      { fr: 'Saute le riz, ajoute pois et œufs.', en: 'Fry rice, add peas and eggs.' },
      { fr: 'Sauce soya, sers.', en: 'Soy sauce, serve.' },
    ],
    nutrition: { cal: 380, p: 14, c: 58, f: 10 },
  }),
  mkRecipe({
    id: 'x_caprese', mealType: 'lunch', minutes: 8, hero: '🍅',
    bg1: '#D89A8A', bg2: '#C26B5A',
    name: { fr: 'Salade caprese', en: 'Caprese salad' },
    tagline: { fr: 'Trois ingrédients, zéro effort', en: 'Three ingredients, zero effort' },
    diet: ['vegetarian', 'glutenFree', 'lowCarb'], allergens: ['dairy'],
    ingredients: [
      { name: { fr: 'Tomates', en: 'Tomatoes' }, emoji: '🍅', qty: { fr: '2', en: '2' } },
      { name: { fr: 'Mozzarella', en: 'Mozzarella' }, emoji: '🧀', qty: { fr: '125 g', en: '125 g' } },
      { name: { fr: 'Basilic', en: 'Basil' }, emoji: '🌿', qty: { fr: 'quelques feuilles', en: 'a few leaves' } },
    ],
    steps: [
      { fr: 'Tranche tomates et mozzarella.', en: 'Slice tomatoes and mozzarella.' },
      { fr: 'Alterne, ajoute basilic, huile, sel.', en: 'Layer, add basil, oil, salt.' },
    ],
    nutrition: { cal: 300, p: 16, c: 8, f: 23 },
  }),
  mkRecipe({
    id: 'x_banana_pancakes', mealType: 'breakfast', minutes: 15, hero: '🥞',
    bg1: '#E8D08A', bg2: '#D8B04A',
    name: { fr: 'Pancakes à la banane', en: 'Banana pancakes' },
    tagline: { fr: 'Sans gluten, 3 ingrédients', en: 'Gluten-free, 3 ingredients' },
    diet: ['vegetarian', 'glutenFree'], allergens: ['eggs'],
    ingredients: [
      { name: { fr: 'Banane', en: 'Banana' }, emoji: '🍌', qty: { fr: '2', en: '2' } },
      { name: { fr: 'Œufs', en: 'Eggs' }, emoji: '🥚', qty: { fr: '2', en: '2' } },
      { name: { fr: 'Cannelle', en: 'Cinnamon' }, emoji: '🟤', qty: { fr: '1 pincée', en: '1 pinch' } },
    ],
    steps: [
      { fr: 'Écrase les bananes, mélange aux œufs.', en: 'Mash bananas, mix with eggs.' },
      { fr: 'Cuis de petites galettes à la poêle.', en: 'Cook small rounds in a pan.' },
    ],
    nutrition: { cal: 230, p: 11, c: 30, f: 8 },
  }),
];

// ── TheMealDB client ────────────────────────────────────────────
// Robustly parse a free-text instructions blob into clean numbered steps.
// TheMealDB data is messy: lone "1." lines, "STEP 1" headers, blank lines,
// or one giant paragraph. We render our own numbering, so strip theirs.
function parseSteps(text) {
  if (!text) return [];
  let lines = String(text).split(/\r?\n/).map(s => s.trim()).filter(Boolean);
  const out = [];
  for (let line of lines) {
    // Drop standalone markers like "1", "1.", "1)", "STEP 1", "Step 1:"
    if (/^(step|étape|etape)?\s*\d+\s*[.):\-]?\s*$/i.test(line)) continue;
    // Strip a leading "1. ", "1) ", "Step 1: ", "1 - " prefix
    line = line.replace(/^(step|étape|etape)?\s*\d+\s*[.):\-]\s*/i, '').trim();
    if (line) out.push({ fr: line, en: line });
  }
  // Fallback: a single dense paragraph → split on sentence boundaries.
  if (out.length <= 1 && lines.length <= 1 && text.length > 180) {
    return String(text).split(/(?<=[.!?])\s+(?=[A-ZÀ-Ý])/).map(s => s.trim())
      .filter(s => s.length > 4).map(s => ({ fr: s, en: s }));
  }
  return out;
}

// ── Quantity scaling (culinary-aware) ───────────────────────────
// Scaling a recipe is NOT a flat rule-of-three. Bulk ingredients scale
// linearly, but seasonings/aromatics/leaveners scale sub-linearly (doubling a
// pot of soup needs ~1.7× the salt, not 2×) and "a pinch / to taste" never
// scales. These exponents follow common test-kitchen guidance.
const _UFRAC = { '½': 0.5, '¼': 0.25, '¾': 0.75, '⅓': 1 / 3, '⅔': 2 / 3, '⅛': 0.125, '⅜': 0.375, '⅝': 0.625, '⅞': 0.875, '⅙': 1 / 6, '⅚': 5 / 6, '⅕': 0.2, '⅖': 0.4, '⅗': 0.6, '⅘': 0.8 };
// Seasonings & leaveners → sub-linear (exponent < 1).
const _SEASON_RE = /\b(sel|salt|poivre|pepper|épice|epice|spice|cannelle|cinnamon|curcuma|turmeric|cumin|paprika|origan|oregano|basilic|basil|persil|parsley|thym|thyme|romarin|rosemary|muscade|nutmeg|gingembre|ginger|cardamome|cardamom|piment|chili|cayenne|clou de girofle|clove|vanille|vanilla|zeste|zest|levure chimique|baking powder|bicarbonate|baking soda|extrait|extract|moutarde|mustard|safran|saffron|coriandre moulue|herbes|herbs|assaisonnement|seasoning)\b/i;
// Things that should never scale.
const _NOSCALE_RE = /\b(pincée|pincee|pinch|au goût|au gout|to taste|garniture|garnish|—|–)\b/i;

function _exponentFor(name) {
  const n = (name || '').toLowerCase();
  if (_NOSCALE_RE.test(n)) return 0;       // keep as-is
  if (_SEASON_RE.test(n)) return 0.80;     // sub-linear
  return 1;                                 // linear (proteins, veg, liquids, grains)
}

function _fmtQty(v) {
  if (!isFinite(v) || v <= 0) return '0';
  if (v >= 10) return String(Math.round(v));
  if (v >= 4) { // halves only for larger counts — avoids "7 ⅜"
    const r = Math.round(v * 2) / 2;
    const w = Math.floor(r); const f = r - w;
    return f ? `${w} ½` : String(w);
  }
  const fracs = [[0, ''], [0.125, '⅛'], [0.25, '¼'], [1 / 3, '⅓'], [0.375, '⅜'], [0.5, '½'], [0.625, '⅝'], [2 / 3, '⅔'], [0.75, '¾'], [0.875, '⅞'], [1, '']];
  const whole = Math.floor(v + 1e-9);
  let frac = v - whole, best = fracs[0], bd = Infinity;
  for (const f of fracs) { const d = Math.abs(frac - f[0]); if (d < bd) { bd = d; best = f; } }
  let w = whole, sym = best[1];
  if (best[0] === 1) { w += 1; sym = ''; }
  if (w === 0 && sym === '') return String(Math.round(v * 100) / 100);
  if (w === 0) return sym;
  return sym ? `${w} ${sym}` : String(w);
}

// Parse a leading numeric token from a qty string → { value, rest } or null.
function _parseLeadingQty(str) {
  const s = String(str).trim();
  // mixed ascii "1 1/2"
  let m = s.match(/^(\d+)\s+(\d+)\/(\d+)\s*/);
  if (m) return { value: +m[1] + (+m[2] / +m[3]), rest: s.slice(m[0].length) };
  // mixed unicode "1 ½" or "1½"
  m = s.match(/^(\d+)\s*([½¼¾⅓⅔⅛⅜⅝⅞⅙⅚⅕⅖⅗⅘])\s*/);
  if (m) return { value: +m[1] + _UFRAC[m[2]], rest: s.slice(m[0].length) };
  // bare unicode "½"
  m = s.match(/^([½¼¾⅓⅔⅛⅜⅝⅞⅙⅚⅕⅖⅗⅘])\s*/);
  if (m) return { value: _UFRAC[m[1]], rest: s.slice(m[0].length) };
  // ascii fraction "1/2"
  m = s.match(/^(\d+)\/(\d+)\s*/);
  if (m) return { value: +m[1] / +m[2], rest: s.slice(m[0].length) };
  // decimal "1.5" / "1,5"
  m = s.match(/^(\d+[.,]\d+)\s*/);
  if (m) return { value: parseFloat(m[1].replace(',', '.')), rest: s.slice(m[0].length) };
  // integer "200"
  m = s.match(/^(\d+)\s*/);
  if (m) return { value: +m[1], rest: s.slice(m[0].length) };
  return null;
}

// Scale a single qty string. factor = targetServings / baseServings.
// Handles ranges ("2-3") by scaling both ends.
function scaleQty(qtyStr, factor, name) {
  if (qtyStr == null) return qtyStr;
  const s = String(qtyStr);
  if (factor === 1 || !isFinite(factor)) return s;
  const exp = _exponentFor((name || '') + ' ' + s);
  if (exp === 0) return s; // pinch / to taste / garnish → unchanged
  const eff = Math.pow(factor, exp);
  // range "2-3 tasses"
  const range = s.match(/^(\d+(?:[.,]\d+)?)\s*[-–]\s*(\d+(?:[.,]\d+)?)(.*)$/);
  if (range) {
    const a = _fmtQty(parseFloat(range[1].replace(',', '.')) * eff);
    const b = _fmtQty(parseFloat(range[2].replace(',', '.')) * eff);
    return `${a}–${b}${range[3]}`;
  }
  const p = _parseLeadingQty(s);
  if (!p) return s; // "quelques feuilles", "au goût" → unchanged
  const rest = p.rest;
  // Re-insert a space before the unit (the parser ate trailing whitespace).
  return _fmtQty(p.value * eff) + (rest && !/^[\s,.;:)]/.test(rest) ? ' ' + rest : rest);
}

// ── Ingredient ↔ inventory matching (works for ALL recipe sources) ──
// Local seed recipes link by `uses` ids; QC/EXTRA and online recipes only have
// ingredient *names*. This unifies both: match by id when present, else by
// normalized name overlap (FR or EN). Used for the "x/y you have" meter,
// smart sorting, and the "add missing to list" action everywhere.
function _norm(s) {
  return (s || '').toString().toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '')
    .replace(/[^a-z0-9 ]/g, ' ').replace(/\s+/g, ' ').trim();
}
const _STOP = new Set(['de', 'la', 'le', 'les', 'des', 'du', 'au', 'aux', 'et', 'a', 'of', 'the', 'and', 'with', 'en', 'cup', 'tasse', 'g', 'ml']);
function _tokens(s) { return _norm(s).split(' ').filter(w => w.length > 2 && !_STOP.has(w)); }
function _invHasIngredient(ing, inventory) {
  if (!ing) return null;
  if (ing.id) { const byId = inventory.find(i => i.id === ing.id); if (byId) return byId; }
  const ingNames = [ing.name && ing.name.fr, ing.name && ing.name.en].filter(Boolean);
  const ingTok = ingNames.flatMap(_tokens);
  if (!ingTok.length) return null;
  for (const inv of inventory) {
    const invNames = [inv.name && inv.name.fr, inv.name && inv.name.en, typeof inv.name === 'string' ? inv.name : null].filter(Boolean);
    const invTok = invNames.flatMap(_tokens);
    if (invTok.some(w => ingTok.includes(w))) return inv;
  }
  return null;
}
// Returns { matched, total, expiringUsed, missing:[ingredients] }
function matchRecipe(recipe, inventory) {
  inventory = inventory || [];
  const ings = (recipe && recipe.ingredients) || [];
  if (ings.length) {
    let matched = 0, expiringUsed = 0; const missing = []; const usedIds = [];
    for (const ing of ings) {
      const hit = _invHasIngredient(ing, inventory);
      if (hit) { matched++; if (hit.id) usedIds.push(hit.id); if (hit.daysLeft != null && hit.daysLeft <= 3) expiringUsed++; }
      else if (ing.pantry) { matched++; }
      else missing.push(window.resolveIngredient ? { ...ing, ...window.resolveIngredient(ing) } : ing);
    }
    return { matched, total: ings.length, expiringUsed, missing, usedIds };
  }
  const uses = (recipe && recipe.uses) || [];
  let matched = 0, expiringUsed = 0; const usedIds = [];
  for (const id of uses) { const hit = inventory.find(i => i.id === id); if (hit) { matched++; usedIds.push(hit.id); if (hit.daysLeft != null && hit.daysLeft <= 3) expiringUsed++; } }
  return { matched, total: Math.max(1, uses.length), expiringUsed, missing: [], usedIds };
}

// Heuristic allergen detection from ingredient names — gives the online
// (TheMealDB) recipes real allergen tags so the same filters work there.
const _ALG_RE = {
  gluten: /\b(flour|farine|bread|pain|pasta|p[âa]tes?|noodle|nouille|wheat|bl[ée]|breadcrumb|chapelure|couscous|barley|orge|cracker|biscuit|soy sauce|sauce soya|beer|bi[èe]re|semolina|semoule)\b/i,
  dairy: /\b(milk|lait|cheese|fromage|butter|beurre|cream|cr[èe]me|yogurt|yogourt|parmesan|mozzarella|cheddar|gruy[èe]re|f[ée]ta|ricotta|ghee)\b/i,
  eggs: /\b(egg|œuf|oeuf|mayonnaise|mayo|meringue|aioli)\b/i,
  nuts: /\b(almond|amande|walnut|noix|pecan|pacane|cashew|cajou|pistach|hazelnut|noisette|pine nut)\b/i,
  peanuts: /\b(peanut|arachide|cacahu[èe]te)\b/i,
  soy: /\b(soy|soja|tofu|edamame|tempeh|miso)\b/i,
  shellfish: /\b(shrimp|crevette|crab|crabe|lobster|homard|prawn|scallop|p[ée]toncle|mussel|moule|clam|palourde|oyster|hu[îi]tre)\b/i,
  fish: /\b(fish|poisson|salmon|saumon|tuna|thon|cod|morue|anchov|anchois|haddock|sardine|trout|truite|tilapia|walleye|dor[ée])\b/i,
  sesame: /\b(sesame|s[ée]same|tahini)\b/i,
};
function inferAllergens(ings) {
  const text = (ings || []).map(i => [i.name && i.name.fr, i.name && i.name.en].filter(Boolean).join(' ')).join(' ');
  const out = [];
  for (const a in _ALG_RE) if (_ALG_RE[a].test(text)) out.push(a);
  return out;
}

// Map a TheMealDB meal record to our recipe shape (image = real photo).
function mealDbToRecipe(meal) {
  if (!meal) return null;
  const ingredients = [];
  for (let i = 1; i <= 20; i++) {
    const ing = (meal['strIngredient' + i] || '').trim();
    const measure = (meal['strMeasure' + i] || '').trim();
    if (ing) ingredients.push({ name: { fr: ing, en: ing }, emoji: '🍽', qty: { fr: measure, en: measure } });
  }
  const steps = parseSteps(meal.strInstructions || '');
  const cat = (meal.strCategory || '').toLowerCase();
  const diet = [];
  if (cat === 'vegetarian') diet.push('vegetarian');
  if (cat === 'vegan') diet.push('vegan', 'vegetarian', 'dairyFree');
  if (cat === 'seafood') diet.push('pescatarian');
  return {
    id: 'mdb_' + meal.idMeal,
    external: true, custom: false,
    name: { fr: meal.strMeal, en: meal.strMeal },
    image: meal.strMealThumb,
    hero: '🍽', bg1: '#A8C895', bg2: '#7FA86A',
    minutes: 30, portions: 2,
    difficulty: { fr: 'Moyen', en: 'Medium' },
    tagline: { fr: meal.strArea ? `Cuisine ${meal.strArea}` : '', en: meal.strArea ? `${meal.strArea} cuisine` : '' },
    mealType: cat === 'breakfast' ? 'breakfast' : 'dinner',
    category: meal.strCategory, area: meal.strArea,
    diet, allergens: inferAllergens(ingredients),
    uses: [], ingredients, steps,
    nutrition: null,
  };
}

const MealDB = {
  base: 'https://www.themealdb.com/api/json/v1/1',
  _get: async (path) => {
    const ctrl = new AbortController();
    const to = setTimeout(() => ctrl.abort(), 8000);
    try {
      const r = await fetch(MealDB.base + path, { signal: ctrl.signal });
      clearTimeout(to);
      return await r.json();
    } catch (e) { clearTimeout(to); return null; }
  },
  search: async (q) => {
    const data = await MealDB._get('/search.php?s=' + encodeURIComponent(q || ''));
    return (data && data.meals ? data.meals : []).map(mealDbToRecipe);
  },
  byCategory: async (cat) => {
    // filter.php returns light records (no instructions); good enough for cards.
    const data = await MealDB._get('/filter.php?c=' + encodeURIComponent(cat));
    return (data && data.meals ? data.meals : []).map(m => ({
      id: 'mdb_' + m.idMeal, external: true, lite: true,
      name: { fr: m.strMeal, en: m.strMeal }, image: m.strMealThumb,
      hero: '🍽', bg1: '#A8C895', bg2: '#7FA86A', minutes: 30, portions: 2,
      difficulty: { fr: 'Moyen', en: 'Medium' }, tagline: { fr: '', en: '' },
      mealType: 'dinner', diet: [], allergens: [], uses: [], ingredients: [], steps: [],
    }));
  },
  byIngredient: async (ing) => {
    const data = await MealDB._get('/filter.php?i=' + encodeURIComponent(ing));
    return (data && data.meals ? data.meals : []).map(m => ({
      id: 'mdb_' + m.idMeal, external: true, lite: true,
      name: { fr: m.strMeal, en: m.strMeal }, image: m.strMealThumb,
      hero: '🍽', bg1: '#A8C895', bg2: '#7FA86A', minutes: 30, portions: 2,
      difficulty: { fr: 'Moyen', en: 'Medium' }, tagline: { fr: '', en: '' },
      mealType: 'dinner', diet: [], allergens: [], uses: [], ingredients: [], steps: [],
    }));
  },
  lookup: async (idMeal) => {
    const id = String(idMeal).replace(/^mdb_/, '');
    const data = await MealDB._get('/lookup.php?i=' + encodeURIComponent(id));
    return data && data.meals && data.meals[0] ? mealDbToRecipe(data.meals[0]) : null;
  },
  categories: async () => {
    const data = await MealDB._get('/categories.php');
    return (data && data.categories ? data.categories : []).map(c => ({
      id: c.idCategory, name: c.strCategory, image: c.strCategoryThumb,
    }));
  },
};

// ── Catalogue québécois (classiques + style Ricardo / weeknight) ──
// Compact builder so we can list a large set without huge boilerplate.
// ings: [ [nameFr, nameEn, qty | [qtyFr,qtyEn]], ... ]
// stepsFr / stepsEn: parallel arrays.
const _QC_PAL = [['#E8C08A', '#D89A4A'], ['#E0A090', '#C26B5A'], ['#B8D89A', '#7FA86A'], ['#D8B48A', '#B5854A'], ['#F0E0A8', '#E8C95C'], ['#C9B6E0', '#9A7FC2']];
function qc(id, nameFr, nameEn, mealType, minutes, hero, ings, stepsFr, stepsEn, opts) {
  opts = opts || {};
  const bg = _QC_PAL[id.length % _QC_PAL.length];
  return mkRecipe({
    id: 'qc_' + id, mealType, minutes, hero, portions: opts.portions || 4,
    bg1: opts.bg1 || bg[0], bg2: opts.bg2 || bg[1],
    name: { fr: nameFr, en: nameEn },
    tagline: { fr: opts.tagFr || 'Classique québécois', en: opts.tagEn || 'Québec classic' },
    difficulty: opts.diff || { fr: 'Facile', en: 'Easy' },
    diet: opts.diet || [], allergens: opts.allergens || [],
    nutrition: opts.nutrition || null,
    ingredients: ings.map(([nf, ne, q]) => ({
      name: { fr: nf, en: ne }, emoji: '🍽',
      qty: Array.isArray(q) ? { fr: q[0], en: q[1] } : { fr: q, en: q },
    })),
    steps: stepsFr.map((s, i) => ({ fr: s, en: (stepsEn[i] || s) })),
  });
}

const QC_RECIPES = [
  qc('pate_chinois', 'Pâté chinois', 'Québec shepherd\'s pie', 'dinner', 45, '🥧',
    [['Bœuf haché', 'Ground beef', '500 g'], ['Maïs en crème', 'Creamed corn', ['1 boîte', '1 can']], ['Maïs en grains', 'Corn kernels', ['1 boîte', '1 can']], ['Pommes de terre', 'Potatoes', '6'], ['Beurre', 'Butter', ['2 c. à soupe', '2 tbsp']], ['Oignon', 'Onion', '1']],
    ['Fais cuire le bœuf avec l\'oignon.', 'Étale le bœuf, puis le maïs, dans un plat.', 'Couvre de purée de pommes de terre.', 'Cuis 30 min à 375 °F, gratine.'],
    ['Brown the beef with the onion.', 'Layer beef then corn in a baking dish.', 'Top with mashed potatoes.', 'Bake 30 min at 375 °F, broil to finish.'],
    { allergens: ['dairy'], nutrition: { cal: 520, p: 28, c: 48, f: 24 }, diff: { fr: 'Facile', en: 'Easy' } }),
  qc('tourtiere', 'Tourtière', 'Tourtière (meat pie)', 'dinner', 75, '🥟',
    [['Porc haché', 'Ground pork', '500 g'], ['Bœuf haché', 'Ground beef', '250 g'], ['Oignon', 'Onion', '1'], ['Pâte à tarte', 'Pie crust', '2'], ['Clou de girofle moulu', 'Ground clove', ['¼ c. à thé', '¼ tsp']], ['Cannelle', 'Cinnamon', ['¼ c. à thé', '¼ tsp']]],
    ['Cuis les viandes avec oignon et épices.', 'Ajoute un peu de bouillon, laisse mijoter 20 min.', 'Garnis la pâte, couvre, dore à l\'œuf.', 'Cuis 45 min à 375 °F.'],
    ['Cook the meats with onion and spices.', 'Add a little broth, simmer 20 min.', 'Fill the crust, cover, egg-wash.', 'Bake 45 min at 375 °F.'],
    { allergens: ['gluten', 'eggs'], nutrition: { cal: 610, p: 26, c: 42, f: 38 } }),
  qc('poutine', 'Poutine', 'Poutine', 'dinner', 30, '🍟',
    [['Pommes de terre', 'Potatoes', '4'], ['Fromage en grains', 'Cheese curds', '200 g'], ['Sauce brune', 'Brown gravy', ['2 tasses', '2 cups']]],
    ['Fais frire ou cuis les frites jusqu\'à dorées.', 'Réchauffe la sauce brune.', 'Garnis les frites de fromage, nappe de sauce chaude.'],
    ['Fry or bake the fries until golden.', 'Heat the brown gravy.', 'Top fries with curds, pour hot gravy over.'],
    { allergens: ['dairy', 'gluten'], nutrition: { cal: 740, p: 22, c: 70, f: 42 } }),
  qc('soupe_pois', 'Soupe aux pois', 'Yellow pea soup', 'lunch', 90, '🍲',
    [['Pois jaunes secs', 'Dried yellow peas', ['2 tasses', '2 cups']], ['Jambon', 'Ham', '1 os'], ['Carottes', 'Carrots', '2'], ['Oignon', 'Onion', '1']],
    ['Fais tremper les pois la veille.', 'Mets tout dans une marmite avec de l\'eau.', 'Mijote 1 h 30 jusqu\'à tendreté.'],
    ['Soak the peas overnight.', 'Put everything in a pot with water.', 'Simmer 1.5 h until tender.'],
    { diet: ['dairyFree', 'highProtein'], nutrition: { cal: 320, p: 20, c: 45, f: 5 } }),
  qc('feves_lard', 'Fèves au lard', 'Baked beans', 'dinner', 240, '🫘',
    [['Haricots blancs', 'Navy beans', ['2 tasses', '2 cups']], ['Lard salé', 'Salt pork', '150 g'], ['Mélasse', 'Molasses', ['½ tasse', '½ cup']], ['Oignon', 'Onion', '1']],
    ['Fais tremper les haricots une nuit.', 'Mélange avec mélasse, lard, oignon.', 'Cuis au four 4 h à 300 °F couvert.'],
    ['Soak the beans overnight.', 'Mix with molasses, pork and onion.', 'Bake 4 h at 300 °F, covered.'],
    { nutrition: { cal: 380, p: 16, c: 58, f: 9 } }),
  qc('ragout_boulettes', 'Ragoût de boulettes', 'Meatball stew', 'dinner', 60, '🍖',
    [['Porc haché', 'Ground pork', '500 g'], ['Farine grillée', 'Browned flour', ['¼ tasse', '¼ cup']], ['Oignon', 'Onion', '1'], ['Bouillon de bœuf', 'Beef broth', ['3 tasses', '3 cups']], ['Cannelle', 'Cinnamon', ['¼ c. à thé', '¼ tsp']]],
    ['Forme et dore les boulettes.', 'Délaye la farine grillée dans le bouillon.', 'Ajoute les boulettes, mijote 40 min.'],
    ['Shape and brown the meatballs.', 'Whisk browned flour into the broth.', 'Add meatballs, simmer 40 min.'],
    { allergens: ['gluten'], nutrition: { cal: 470, p: 28, c: 24, f: 28 } }),
  qc('macaroni_viande', 'Macaroni à la viande', 'Beef macaroni', 'dinner', 30, '🍝',
    [['Macaroni', 'Macaroni', '400 g'], ['Bœuf haché', 'Ground beef', '400 g'], ['Tomates en dés', 'Diced tomatoes', ['1 boîte', '1 can']], ['Oignon', 'Onion', '1']],
    ['Cuis les pâtes.', 'Dore le bœuf avec l\'oignon.', 'Ajoute les tomates, mijote 10 min, mélange aux pâtes.'],
    ['Cook the pasta.', 'Brown the beef with the onion.', 'Add tomatoes, simmer 10 min, toss with pasta.'],
    { allergens: ['gluten'], nutrition: { cal: 520, p: 30, c: 62, f: 16 } }),
  qc('pain_viande', 'Pain de viande', 'Meatloaf', 'dinner', 70, '🍞',
    [['Bœuf haché', 'Ground beef', '700 g'], ['Œuf', 'Egg', '1'], ['Chapelure', 'Breadcrumbs', ['½ tasse', '½ cup']], ['Ketchup', 'Ketchup', ['¼ tasse', '¼ cup']], ['Oignon', 'Onion', '1']],
    ['Mélange tous les ingrédients.', 'Forme un pain dans un moule.', 'Glace de ketchup, cuis 1 h à 350 °F.'],
    ['Mix all ingredients.', 'Shape into a loaf in a pan.', 'Top with ketchup, bake 1 h at 350 °F.'],
    { allergens: ['gluten', 'eggs'], nutrition: { cal: 480, p: 32, c: 18, f: 30 } }),
  qc('cretons', 'Cretons', 'Pork cretons', 'breakfast', 75, '🥓',
    [['Porc haché', 'Ground pork', '500 g'], ['Lait', 'Milk', ['1 tasse', '1 cup']], ['Oignon', 'Onion', '1'], ['Chapelure', 'Breadcrumbs', ['½ tasse', '½ cup']], ['Clou de girofle', 'Clove', ['¼ c. à thé', '¼ tsp']]],
    ['Mijote le porc avec lait, oignon et épices 1 h.', 'Ajoute la chapelure pour lier.', 'Verse en moule, réfrigère.'],
    ['Simmer pork with milk, onion and spices 1 h.', 'Stir in breadcrumbs to bind.', 'Pour into a mold, chill.'],
    { allergens: ['gluten', 'dairy'], nutrition: { cal: 260, p: 14, c: 8, f: 19 } }),
  qc('spaghetti_sauce', 'Spaghetti sauce à la viande', 'Spaghetti bolognese', 'dinner', 45, '🍝',
    [['Spaghetti', 'Spaghetti', '400 g'], ['Bœuf haché', 'Ground beef', '450 g'], ['Sauce tomate', 'Tomato sauce', ['1 pot', '1 jar']], ['Oignon', 'Onion', '1'], ['Ail', 'Garlic', ['2 gousses', '2 cloves']]],
    ['Dore le bœuf, oignon et ail.', 'Ajoute la sauce, mijote 20 min.', 'Sers sur les pâtes cuites.'],
    ['Brown beef, onion and garlic.', 'Add sauce, simmer 20 min.', 'Serve over cooked pasta.'],
    { allergens: ['gluten'], nutrition: { cal: 560, p: 30, c: 68, f: 18 } }),
  qc('boeuf_legumes', 'Bœuf aux légumes mijoté', 'Beef & vegetable stew', 'dinner', 150, '🥘',
    [['Cubes de bœuf', 'Beef cubes', '700 g'], ['Carottes', 'Carrots', '3'], ['Pommes de terre', 'Potatoes', '4'], ['Bouillon de bœuf', 'Beef broth', ['4 tasses', '4 cups']], ['Oignon', 'Onion', '1']],
    ['Dore les cubes de bœuf.', 'Ajoute légumes et bouillon.', 'Mijote 2 h jusqu\'à tendreté.'],
    ['Brown the beef cubes.', 'Add vegetables and broth.', 'Simmer 2 h until tender.'],
    { diet: ['dairyFree', 'highProtein'], nutrition: { cal: 440, p: 38, c: 32, f: 16 } }),
  qc('roti_porc', 'Rôti de porc et patates brunes', 'Pork roast with potatoes', 'dinner', 120, '🍖',
    [['Rôti de porc', 'Pork roast', '1,5 kg'], ['Pommes de terre', 'Potatoes', '6'], ['Oignon', 'Onion', '2'], ['Bouillon', 'Broth', ['2 tasses', '2 cups']]],
    ['Assaisonne et dore le rôti.', 'Dépose patates et oignons autour.', 'Cuis 2 h à 325 °F en arrosant.'],
    ['Season and sear the roast.', 'Place potatoes and onions around.', 'Roast 2 h at 325 °F, basting.'],
    { diet: ['dairyFree'], nutrition: { cal: 560, p: 42, c: 30, f: 28 } }),
  qc('saumon_erable', 'Saumon à l\'érable', 'Maple-glazed salmon', 'dinner', 25, '🐟',
    [['Filets de saumon', 'Salmon fillets', '4'], ['Sirop d\'érable', 'Maple syrup', ['¼ tasse', '¼ cup']], ['Sauce soya', 'Soy sauce', ['2 c. à soupe', '2 tbsp']], ['Ail', 'Garlic', ['1 gousse', '1 clove']]],
    ['Mélange érable, soya et ail.', 'Badigeonne le saumon.', 'Cuis 15 min à 400 °F.'],
    ['Mix maple, soy and garlic.', 'Brush over the salmon.', 'Bake 15 min at 400 °F.'],
    { diet: ['pescatarian', 'dairyFree', 'highProtein'], allergens: ['fish', 'soy'], nutrition: { cal: 380, p: 34, c: 14, f: 20 } }),
  qc('cotelettes_porc', 'Côtelettes de porc et compote', 'Pork chops with applesauce', 'dinner', 30, '🍖',
    [['Côtelettes de porc', 'Pork chops', '4'], ['Compote de pommes', 'Applesauce', ['1 tasse', '1 cup']], ['Beurre', 'Butter', ['1 c. à soupe', '1 tbsp']]],
    ['Assaisonne et poêle les côtelettes 4 min par côté.', 'Laisse reposer.', 'Sers avec la compote.'],
    ['Season and pan-fry chops 4 min per side.', 'Let rest.', 'Serve with applesauce.'],
    { allergens: ['dairy'], diet: ['glutenFree'], nutrition: { cal: 410, p: 36, c: 16, f: 22 } }),
  qc('poulet_roti', 'Poulet rôti au beurre', 'Butter roast chicken', 'dinner', 90, '🍗',
    [['Poulet entier', 'Whole chicken', '1'], ['Beurre', 'Butter', ['3 c. à soupe', '3 tbsp']], ['Ail', 'Garlic', ['4 gousses', '4 cloves']], ['Citron', 'Lemon', '1']],
    ['Frotte le poulet de beurre et d\'ail.', 'Glisse le citron à l\'intérieur.', 'Rôtis 1 h 15 à 375 °F.'],
    ['Rub chicken with butter and garlic.', 'Tuck lemon inside.', 'Roast 1 h 15 at 375 °F.'],
    { allergens: ['dairy'], diet: ['glutenFree', 'highProtein'], nutrition: { cal: 520, p: 46, c: 2, f: 36 } }),
  qc('pate_poulet', 'Pâté au poulet', 'Chicken pot pie', 'dinner', 60, '🥧',
    [['Poulet cuit', 'Cooked chicken', ['3 tasses', '3 cups']], ['Légumes mélangés', 'Mixed vegetables', ['2 tasses', '2 cups']], ['Crème de poulet', 'Cream of chicken', ['1 boîte', '1 can']], ['Pâte à tarte', 'Pie crust', '2']],
    ['Mélange poulet, légumes et crème.', 'Garnis la pâte, couvre.', 'Cuis 40 min à 375 °F.'],
    ['Mix chicken, vegetables and cream.', 'Fill the crust, cover.', 'Bake 40 min at 375 °F.'],
    { allergens: ['gluten', 'dairy'], nutrition: { cal: 540, p: 28, c: 40, f: 30 } }),
  qc('chili', 'Chili con carne', 'Chili con carne', 'dinner', 45, '🌶️',
    [['Bœuf haché', 'Ground beef', '500 g'], ['Haricots rouges', 'Red kidney beans', ['1 boîte', '1 can']], ['Tomates en dés', 'Diced tomatoes', ['1 boîte', '1 can']], ['Poudre de chili', 'Chili powder', ['2 c. à soupe', '2 tbsp']], ['Oignon', 'Onion', '1']],
    ['Dore le bœuf avec l\'oignon.', 'Ajoute tomates, haricots, épices.', 'Mijote 30 min.'],
    ['Brown beef with onion.', 'Add tomatoes, beans, spices.', 'Simmer 30 min.'],
    { diet: ['dairyFree', 'glutenFree', 'highProtein'], nutrition: { cal: 430, p: 30, c: 34, f: 18 } }),
  qc('tacos_boeuf', 'Tacos au bœuf', 'Beef tacos', 'dinner', 25, '🌮',
    [['Bœuf haché', 'Ground beef', '450 g'], ['Coquilles à taco', 'Taco shells', '8'], ['Laitue', 'Lettuce', ['2 tasses', '2 cups']], ['Fromage râpé', 'Shredded cheese', ['1 tasse', '1 cup']], ['Salsa', 'Salsa', ['½ tasse', '½ cup']]],
    ['Dore le bœuf avec un sachet d\'épices.', 'Réchauffe les coquilles.', 'Garnis de bœuf, laitue, fromage, salsa.'],
    ['Brown the beef with a seasoning packet.', 'Warm the shells.', 'Fill with beef, lettuce, cheese, salsa.'],
    { allergens: ['gluten', 'dairy'], nutrition: { cal: 460, p: 26, c: 34, f: 24 } }),
  qc('general_tao', 'Poulet Général Tao', 'General Tao chicken', 'dinner', 35, '🍗',
    [['Poitrines de poulet', 'Chicken breasts', '2'], ['Fécule de maïs', 'Cornstarch', ['½ tasse', '½ cup']], ['Sauce Général Tao', 'General Tao sauce', ['¾ tasse', '¾ cup']], ['Riz', 'Rice', ['2 tasses', '2 cups']]],
    ['Enrobe le poulet de fécule, fais frire.', 'Réchauffe la sauce, enrobe le poulet.', 'Sers sur le riz.'],
    ['Coat chicken in cornstarch, fry.', 'Heat sauce, toss chicken in it.', 'Serve over rice.'],
    { allergens: ['soy', 'gluten'], diet: ['dairyFree', 'highProtein'], nutrition: { cal: 600, p: 38, c: 70, f: 16 } }),
  qc('riz_frit', 'Riz frit maison', 'Homemade fried rice', 'lunch', 20, '🍚',
    [['Riz cuit', 'Cooked rice', ['3 tasses', '3 cups']], ['Œufs', 'Eggs', '2'], ['Légumes surgelés', 'Frozen vegetables', ['1 tasse', '1 cup']], ['Sauce soya', 'Soy sauce', ['3 c. à soupe', '3 tbsp']]],
    ['Brouille les œufs, réserve.', 'Saute riz et légumes.', 'Ajoute œufs et soya, mélange.'],
    ['Scramble the eggs, set aside.', 'Stir-fry rice and vegetables.', 'Add eggs and soy, toss.'],
    { allergens: ['eggs', 'soy'], diet: ['vegetarian', 'dairyFree'], nutrition: { cal: 360, p: 12, c: 58, f: 9 } }),
  qc('quiche_lorraine', 'Quiche lorraine', 'Quiche Lorraine', 'lunch', 50, '🥧',
    [['Pâte à tarte', 'Pie crust', '1'], ['Bacon', 'Bacon', '6 tranches'], ['Œufs', 'Eggs', '4'], ['Crème', 'Cream', ['1 tasse', '1 cup']], ['Fromage suisse', 'Swiss cheese', ['1 tasse', '1 cup']]],
    ['Cuis le bacon, émiette.', 'Bats œufs et crème, ajoute fromage et bacon.', 'Verse sur la pâte, cuis 35 min à 375 °F.'],
    ['Cook bacon, crumble.', 'Whisk eggs and cream, add cheese and bacon.', 'Pour into crust, bake 35 min at 375 °F.'],
    { allergens: ['gluten', 'eggs', 'dairy'], nutrition: { cal: 480, p: 18, c: 22, f: 36 } }),
  qc('omelette_western', 'Omelette western', 'Western omelette', 'breakfast', 15, '🍳',
    [['Œufs', 'Eggs', '3'], ['Jambon', 'Ham', ['½ tasse', '½ cup']], ['Poivron', 'Bell pepper', '½'], ['Oignon', 'Onion', '¼'], ['Fromage', 'Cheese', ['¼ tasse', '¼ cup']]],
    ['Saute jambon, poivron et oignon.', 'Verse les œufs battus.', 'Ajoute le fromage, plie, sers.'],
    ['Sauté ham, pepper and onion.', 'Pour in beaten eggs.', 'Add cheese, fold, serve.'],
    { allergens: ['eggs', 'dairy'], diet: ['glutenFree', 'highProtein'], nutrition: { cal: 340, p: 26, c: 6, f: 24 } }),
  qc('crepes', 'Crêpes', 'Crêpes', 'breakfast', 25, '🥞',
    [['Farine', 'Flour', ['1 tasse', '1 cup']], ['Lait', 'Milk', ['1 ½ tasse', '1 ½ cup']], ['Œufs', 'Eggs', '2'], ['Beurre fondu', 'Melted butter', ['2 c. à soupe', '2 tbsp']]],
    ['Fouette tous les ingrédients en pâte lisse.', 'Verse une louche dans une poêle chaude.', 'Cuis 1 min par côté.'],
    ['Whisk everything into a smooth batter.', 'Pour a ladle into a hot pan.', 'Cook 1 min per side.'],
    { allergens: ['gluten', 'eggs', 'dairy'], diet: ['vegetarian'], nutrition: { cal: 220, p: 8, c: 28, f: 9 } }),
  qc('pain_dore', 'Pain doré', 'French toast', 'breakfast', 15, '🍞',
    [['Pain', 'Bread', '6 tranches'], ['Œufs', 'Eggs', '3'], ['Lait', 'Milk', ['½ tasse', '½ cup']], ['Cannelle', 'Cinnamon', ['½ c. à thé', '½ tsp']], ['Sirop d\'érable', 'Maple syrup', ['au goût', 'to taste']]],
    ['Bats œufs, lait et cannelle.', 'Trempe le pain, poêle au beurre.', 'Sers avec sirop d\'érable.'],
    ['Whisk eggs, milk and cinnamon.', 'Dip bread, pan-fry in butter.', 'Serve with maple syrup.'],
    { allergens: ['gluten', 'eggs', 'dairy'], diet: ['vegetarian'], nutrition: { cal: 320, p: 12, c: 44, f: 10 } }),
  qc('gaufres', 'Gaufres', 'Waffles', 'breakfast', 25, '🧇',
    [['Farine', 'Flour', ['2 tasses', '2 cups']], ['Lait', 'Milk', ['1 ¾ tasse', '1 ¾ cup']], ['Œufs', 'Eggs', '2'], ['Poudre à pâte', 'Baking powder', ['1 c. à soupe', '1 tbsp']], ['Huile', 'Oil', ['⅓ tasse', '⅓ cup']]],
    ['Mélange les secs, puis les liquides.', 'Verse dans le gaufrier chaud.', 'Cuis jusqu\'à doré.'],
    ['Mix dry, then wet ingredients.', 'Pour into a hot waffle iron.', 'Cook until golden.'],
    { allergens: ['gluten', 'eggs', 'dairy'], diet: ['vegetarian'], nutrition: { cal: 290, p: 8, c: 38, f: 12 } }),
  qc('muffins_bleuets', 'Muffins aux bleuets', 'Blueberry muffins', 'breakfast', 30, '🧁',
    [['Farine', 'Flour', ['2 tasses', '2 cups']], ['Sucre', 'Sugar', ['¾ tasse', '¾ cup']], ['Bleuets', 'Blueberries', ['1 tasse', '1 cup']], ['Œuf', 'Egg', '1'], ['Lait', 'Milk', ['1 tasse', '1 cup']], ['Poudre à pâte', 'Baking powder', ['2 c. à thé', '2 tsp']]],
    ['Mélange secs et liquides séparément.', 'Combine, ajoute les bleuets.', 'Cuis 22 min à 375 °F.'],
    ['Mix dry and wet separately.', 'Combine, fold in blueberries.', 'Bake 22 min at 375 °F.'],
    { allergens: ['gluten', 'eggs', 'dairy'], diet: ['vegetarian'], portions: 12, nutrition: { cal: 210, p: 4, c: 36, f: 6 } }),
  qc('pouding_chomeur', 'Pouding chômeur', 'Pouding chômeur', 'dinner', 45, '🍮',
    [['Farine', 'Flour', ['1 tasse', '1 cup']], ['Sucre', 'Sugar', ['½ tasse', '½ cup']], ['Lait', 'Milk', ['½ tasse', '½ cup']], ['Cassonade', 'Brown sugar', ['1 tasse', '1 cup']], ['Crème', 'Cream', ['1 tasse', '1 cup']]],
    ['Prépare une pâte à gâteau simple, verse en plat.', 'Fais bouillir cassonade et crème, verse dessus.', 'Cuis 35 min à 350 °F.'],
    ['Make a simple cake batter, pour into a dish.', 'Boil brown sugar and cream, pour over.', 'Bake 35 min at 350 °F.'],
    { allergens: ['gluten', 'dairy'], diet: ['vegetarian'], portions: 6, nutrition: { cal: 380, p: 4, c: 64, f: 13 } }),
  qc('tarte_sucre', 'Tarte au sucre', 'Sugar pie', 'dinner', 45, '🥧',
    [['Pâte à tarte', 'Pie crust', '1'], ['Cassonade', 'Brown sugar', ['1 ½ tasse', '1 ½ cup']], ['Crème', 'Cream', ['1 tasse', '1 cup']], ['Farine', 'Flour', ['3 c. à soupe', '3 tbsp']]],
    ['Mélange cassonade, crème et farine.', 'Verse dans la pâte.', 'Cuis 40 min à 350 °F.'],
    ['Mix brown sugar, cream and flour.', 'Pour into the crust.', 'Bake 40 min at 350 °F.'],
    { allergens: ['gluten', 'dairy'], diet: ['vegetarian'], portions: 8, nutrition: { cal: 410, p: 3, c: 62, f: 17 } }),
  qc('sucre_creme', 'Sucre à la crème', 'Fudge (sucre à la crème)', 'dinner', 20, '🍬',
    [['Cassonade', 'Brown sugar', ['2 tasses', '2 cups']], ['Crème 35 %', 'Heavy cream', ['1 tasse', '1 cup']], ['Beurre', 'Butter', ['¼ tasse', '¼ cup']]],
    ['Fais bouillir tous les ingrédients à 115 °C.', 'Bats jusqu\'à épaississement.', 'Verse en moule, laisse prendre.'],
    ['Boil all ingredients to 115 °C.', 'Beat until thick.', 'Pour into a pan, let set.'],
    { allergens: ['dairy'], diet: ['vegetarian', 'glutenFree'], portions: 16, nutrition: { cal: 150, p: 1, c: 24, f: 6 } }),
  qc('tarte_pommes', 'Tarte aux pommes', 'Apple pie', 'dinner', 70, '🥧',
    [['Pâte à tarte', 'Pie crust', '2'], ['Pommes', 'Apples', '6'], ['Sucre', 'Sugar', ['½ tasse', '½ cup']], ['Cannelle', 'Cinnamon', ['1 c. à thé', '1 tsp']]],
    ['Tranche les pommes, mélange sucre et cannelle.', 'Garnis la pâte, couvre.', 'Cuis 50 min à 375 °F.'],
    ['Slice apples, toss with sugar and cinnamon.', 'Fill the crust, cover.', 'Bake 50 min at 375 °F.'],
    { allergens: ['gluten'], diet: ['vegetarian', 'dairyFree'], portions: 8, nutrition: { cal: 320, p: 3, c: 52, f: 12 } }),
  qc('carres_dattes', 'Carrés aux dattes', 'Date squares', 'dinner', 45, '🍪',
    [['Dattes', 'Dates', ['2 tasses', '2 cups']], ['Flocons d\'avoine', 'Rolled oats', ['1 ½ tasse', '1 ½ cup']], ['Farine', 'Flour', ['1 tasse', '1 cup']], ['Cassonade', 'Brown sugar', ['1 tasse', '1 cup']], ['Beurre', 'Butter', ['¾ tasse', '¾ cup']]],
    ['Mijote les dattes avec un peu d\'eau en purée.', 'Mélange avoine, farine, cassonade, beurre.', 'Étage, cuis 30 min à 350 °F.'],
    ['Simmer dates with a little water into a paste.', 'Mix oats, flour, brown sugar, butter.', 'Layer, bake 30 min at 350 °F.'],
    { allergens: ['gluten', 'dairy'], diet: ['vegetarian'], portions: 12, nutrition: { cal: 280, p: 3, c: 46, f: 11 } }),
  qc('biscuits_choco', 'Biscuits aux brisures de chocolat', 'Chocolate chip cookies', 'dinner', 25, '🍪',
    [['Farine', 'Flour', ['2 ¼ tasse', '2 ¼ cup']], ['Beurre', 'Butter', ['1 tasse', '1 cup']], ['Cassonade', 'Brown sugar', ['¾ tasse', '¾ cup']], ['Œufs', 'Eggs', '2'], ['Brisures de chocolat', 'Chocolate chips', ['2 tasses', '2 cups']]],
    ['Crème le beurre et le sucre.', 'Ajoute œufs, farine, brisures.', 'Cuis 11 min à 350 °F.'],
    ['Cream butter and sugar.', 'Add eggs, flour, chips.', 'Bake 11 min at 350 °F.'],
    { allergens: ['gluten', 'eggs', 'dairy'], diet: ['vegetarian'], portions: 24, nutrition: { cal: 160, p: 2, c: 22, f: 8 } }),
  qc('gateau_carottes', 'Gâteau aux carottes', 'Carrot cake', 'dinner', 60, '🥕',
    [['Farine', 'Flour', ['2 tasses', '2 cups']], ['Carottes râpées', 'Grated carrots', ['2 tasses', '2 cups']], ['Sucre', 'Sugar', ['1 ½ tasse', '1 ½ cup']], ['Œufs', 'Eggs', '3'], ['Huile', 'Oil', ['1 tasse', '1 cup']], ['Cannelle', 'Cinnamon', ['2 c. à thé', '2 tsp']]],
    ['Mélange secs et liquides, ajoute les carottes.', 'Verse en moule.', 'Cuis 45 min à 350 °F, glace au fromage.'],
    ['Mix dry and wet, fold in carrots.', 'Pour into a pan.', 'Bake 45 min at 350 °F, frost with cream cheese.'],
    { allergens: ['gluten', 'eggs'], diet: ['vegetarian'], portions: 12, nutrition: { cal: 340, p: 4, c: 44, f: 17 } }),
  qc('tarte_erable', 'Tarte au sirop d\'érable', 'Maple syrup pie', 'dinner', 45, '🍁',
    [['Pâte à tarte', 'Pie crust', '1'], ['Sirop d\'érable', 'Maple syrup', ['1 ½ tasse', '1 ½ cup']], ['Crème', 'Cream', ['½ tasse', '½ cup']], ['Fécule de maïs', 'Cornstarch', ['3 c. à soupe', '3 tbsp']]],
    ['Chauffe sirop et crème.', 'Épaissis avec la fécule délayée.', 'Verse dans la pâte, cuis 25 min à 350 °F.'],
    ['Heat syrup and cream.', 'Thicken with slurried cornstarch.', 'Pour into crust, bake 25 min at 350 °F.'],
    { allergens: ['gluten', 'dairy'], diet: ['vegetarian'], portions: 8, nutrition: { cal: 360, p: 2, c: 58, f: 13 } }),
  qc('brownies', 'Brownies', 'Brownies', 'dinner', 35, '🍫',
    [['Chocolat noir', 'Dark chocolate', '200 g'], ['Beurre', 'Butter', ['¾ tasse', '¾ cup']], ['Sucre', 'Sugar', ['1 ½ tasse', '1 ½ cup']], ['Œufs', 'Eggs', '3'], ['Farine', 'Flour', ['1 tasse', '1 cup']]],
    ['Fais fondre chocolat et beurre.', 'Ajoute sucre, œufs, farine.', 'Cuis 25 min à 350 °F.'],
    ['Melt chocolate and butter.', 'Add sugar, eggs, flour.', 'Bake 25 min at 350 °F.'],
    { allergens: ['gluten', 'eggs', 'dairy'], diet: ['vegetarian'], portions: 16, nutrition: { cal: 250, p: 3, c: 30, f: 14 } }),
  qc('salade_macaroni', 'Salade de macaroni', 'Macaroni salad', 'lunch', 20, '🥗',
    [['Macaroni', 'Macaroni', '350 g'], ['Mayonnaise', 'Mayonnaise', ['¾ tasse', '¾ cup']], ['Céleri', 'Celery', '2 branches'], ['Poivron', 'Bell pepper', '1'], ['Oignon vert', 'Green onion', '2']],
    ['Cuis et refroidis les pâtes.', 'Coupe les légumes.', 'Mélange tout avec la mayo.'],
    ['Cook and cool the pasta.', 'Dice the vegetables.', 'Mix everything with mayo.'],
    { allergens: ['gluten', 'eggs'], diet: ['vegetarian'], nutrition: { cal: 380, p: 8, c: 48, f: 18 } }),
  qc('sandwich_oeufs', 'Sandwich aux œufs', 'Egg salad sandwich', 'lunch', 15, '🥪',
    [['Œufs', 'Eggs', '4'], ['Mayonnaise', 'Mayonnaise', ['3 c. à soupe', '3 tbsp']], ['Pain', 'Bread', '4 tranches'], ['Oignon vert', 'Green onion', '1']],
    ['Fais cuire les œufs durs, écrase.', 'Mélange avec mayo et oignon vert.', 'Garnis le pain.'],
    ['Hard-boil the eggs, mash.', 'Mix with mayo and green onion.', 'Fill the bread.'],
    { allergens: ['gluten', 'eggs'], diet: ['vegetarian'], nutrition: { cal: 360, p: 16, c: 30, f: 20 } }),
  qc('club_sandwich', 'Club sandwich', 'Club sandwich', 'lunch', 20, '🥪',
    [['Pain grillé', 'Toast', '3 tranches'], ['Poulet cuit', 'Cooked chicken', '100 g'], ['Bacon', 'Bacon', '3 tranches'], ['Tomate', 'Tomato', '1'], ['Laitue', 'Lettuce', '2 feuilles'], ['Mayonnaise', 'Mayonnaise', ['2 c. à soupe', '2 tbsp']]],
    ['Grille le pain, cuis le bacon.', 'Monte en deux étages.', 'Coupe en quatre, pique.'],
    ['Toast bread, cook bacon.', 'Stack into two layers.', 'Cut in four, pin.'],
    { allergens: ['gluten', 'eggs'], nutrition: { cal: 560, p: 32, c: 38, f: 30 } }),
  qc('salade_cesar', 'Salade César', 'Caesar salad', 'lunch', 15, '🥗',
    [['Laitue romaine', 'Romaine lettuce', '1'], ['Croûtons', 'Croutons', ['1 tasse', '1 cup']], ['Parmesan', 'Parmesan', ['½ tasse', '½ cup']], ['Vinaigrette César', 'Caesar dressing', ['⅓ tasse', '⅓ cup']], ['Bacon', 'Bacon', '4 tranches']],
    ['Coupe la laitue.', 'Ajoute croûtons, parmesan, bacon.', 'Mélange avec la vinaigrette.'],
    ['Chop the lettuce.', 'Add croutons, parmesan, bacon.', 'Toss with dressing.'],
    { allergens: ['gluten', 'dairy', 'eggs', 'fish'], nutrition: { cal: 340, p: 14, c: 16, f: 24 } }),
  qc('soupe_oignon', 'Soupe à l\'oignon gratinée', 'French onion soup', 'dinner', 60, '🧅',
    [['Oignons', 'Onions', '5'], ['Bouillon de bœuf', 'Beef broth', ['6 tasses', '6 cups']], ['Pain baguette', 'Baguette', '4 tranches'], ['Gruyère', 'Gruyère', ['1 tasse', '1 cup']], ['Beurre', 'Butter', ['2 c. à soupe', '2 tbsp']]],
    ['Caramélise les oignons 25 min.', 'Ajoute le bouillon, mijote 20 min.', 'Couvre de pain et fromage, gratine.'],
    ['Caramelize onions 25 min.', 'Add broth, simmer 20 min.', 'Top with bread and cheese, broil.'],
    { allergens: ['gluten', 'dairy'], diet: ['vegetarian'], nutrition: { cal: 360, p: 16, c: 34, f: 18 } }),
  qc('soupe_legumes', 'Soupe aux légumes', 'Vegetable soup', 'lunch', 40, '🍲',
    [['Carottes', 'Carrots', '2'], ['Céleri', 'Celery', '2 branches'], ['Pommes de terre', 'Potatoes', '2'], ['Tomates en dés', 'Diced tomatoes', ['1 boîte', '1 can']], ['Bouillon', 'Broth', ['6 tasses', '6 cups']]],
    ['Coupe tous les légumes.', 'Fais revenir, ajoute bouillon et tomates.', 'Mijote 30 min.'],
    ['Dice all the vegetables.', 'Sauté, add broth and tomatoes.', 'Simmer 30 min.'],
    { diet: ['vegan', 'vegetarian', 'dairyFree', 'glutenFree'], nutrition: { cal: 160, p: 5, c: 30, f: 3 } }),
  qc('grilled_cheese', 'Grilled cheese', 'Grilled cheese', 'lunch', 10, '🧀',
    [['Pain', 'Bread', '2 tranches'], ['Fromage cheddar', 'Cheddar cheese', '2 tranches'], ['Beurre', 'Butter', ['1 c. à soupe', '1 tbsp']]],
    ['Beurre l\'extérieur des tranches.', 'Garnis de fromage.', 'Poêle 3 min par côté.'],
    ['Butter the outside of the bread.', 'Fill with cheese.', 'Pan-fry 3 min per side.'],
    { allergens: ['gluten', 'dairy'], diet: ['vegetarian'], portions: 1, nutrition: { cal: 400, p: 16, c: 30, f: 24 } }),
  qc('pizza_maison', 'Pizza maison', 'Homemade pizza', 'dinner', 30, '🍕',
    [['Pâte à pizza', 'Pizza dough', '1'], ['Sauce tomate', 'Tomato sauce', ['½ tasse', '½ cup']], ['Mozzarella', 'Mozzarella', ['2 tasses', '2 cups']], ['Pepperoni', 'Pepperoni', '20 tranches']],
    ['Étale la pâte, garnis de sauce.', 'Ajoute fromage et garnitures.', 'Cuis 15 min à 450 °F.'],
    ['Roll the dough, spread sauce.', 'Add cheese and toppings.', 'Bake 15 min at 450 °F.'],
    { allergens: ['gluten', 'dairy'], nutrition: { cal: 560, p: 26, c: 58, f: 24 } }),
  qc('jambon_erable', 'Jambon à l\'érable', 'Maple-glazed ham', 'dinner', 150, '🍖',
    [['Jambon', 'Ham', '2 kg'], ['Sirop d\'érable', 'Maple syrup', ['1 tasse', '1 cup']], ['Moutarde de Dijon', 'Dijon mustard', ['2 c. à soupe', '2 tbsp']]],
    ['Dépose le jambon dans une rôtissoire.', 'Badigeonne d\'érable et moutarde.', 'Cuis 2 h à 325 °F en arrosant.'],
    ['Place ham in a roasting pan.', 'Brush with maple and mustard.', 'Bake 2 h at 325 °F, basting.'],
    { allergens: ['mustard'], diet: ['dairyFree', 'glutenFree', 'highProtein'], portions: 8, nutrition: { cal: 360, p: 34, c: 18, f: 16 } }),
  qc('dore_amandine', 'Doré amandine', 'Walleye amandine', 'dinner', 25, '🐟',
    [['Filets de doré', 'Walleye fillets', '4'], ['Amandes effilées', 'Sliced almonds', ['½ tasse', '½ cup']], ['Beurre', 'Butter', ['3 c. à soupe', '3 tbsp']], ['Citron', 'Lemon', '1']],
    ['Poêle les filets au beurre.', 'Fais dorer les amandes dans le beurre.', 'Nappe le poisson, ajoute le citron.'],
    ['Pan-fry the fillets in butter.', 'Toast the almonds in butter.', 'Spoon over fish, add lemon.'],
    { allergens: ['fish', 'nuts', 'dairy'], diet: ['pescatarian', 'glutenFree', 'highProtein'], nutrition: { cal: 380, p: 32, c: 6, f: 26 } }),
  qc('boeuf_bourguignon', 'Bœuf bourguignon', 'Beef bourguignon', 'dinner', 180, '🍷',
    [['Cubes de bœuf', 'Beef cubes', '800 g'], ['Vin rouge', 'Red wine', ['2 tasses', '2 cups']], ['Champignons', 'Mushrooms', ['2 tasses', '2 cups']], ['Carottes', 'Carrots', '3'], ['Bacon', 'Bacon', '4 tranches']],
    ['Dore bœuf et bacon.', 'Ajoute vin, légumes et bouillon.', 'Mijote 2 h 30 au four à 325 °F.'],
    ['Brown beef and bacon.', 'Add wine, vegetables and broth.', 'Braise 2.5 h in the oven at 325 °F.'],
    { diet: ['dairyFree', 'highProtein'], nutrition: { cal: 520, p: 42, c: 14, f: 28 } }),
  qc('poutine_dejeuner', 'Poutine déjeuner', 'Breakfast poutine', 'breakfast', 25, '🍳',
    [['Pommes de terre rissolées', 'Hash browns', ['3 tasses', '3 cups']], ['Fromage en grains', 'Cheese curds', ['1 tasse', '1 cup']], ['Œufs', 'Eggs', '4'], ['Sauce hollandaise', 'Hollandaise sauce', ['1 tasse', '1 cup']]],
    ['Fais dorer les pommes de terre.', 'Pochs les œufs.', 'Garnis de fromage, œufs, hollandaise.'],
    ['Crisp up the hash browns.', 'Poach the eggs.', 'Top with curds, eggs, hollandaise.'],
    { allergens: ['dairy', 'eggs'], diet: ['vegetarian', 'glutenFree'], nutrition: { cal: 620, p: 22, c: 44, f: 40 } }),
  qc('smoothie_dejeuner', 'Smoothie déjeuner', 'Breakfast smoothie', 'breakfast', 5, '🥤',
    [['Banane', 'Banana', '1'], ['Petits fruits surgelés', 'Frozen berries', ['1 tasse', '1 cup']], ['Yogourt grec', 'Greek yogurt', ['½ tasse', '½ cup']], ['Lait', 'Milk', ['1 tasse', '1 cup']]],
    ['Mets tout au mélangeur.', 'Mixe jusqu\'à lisse.', 'Sers froid.'],
    ['Put everything in a blender.', 'Blend until smooth.', 'Serve cold.'],
    { allergens: ['dairy'], diet: ['vegetarian', 'glutenFree', 'highProtein'], portions: 2, nutrition: { cal: 220, p: 12, c: 36, f: 4 } }),
  qc('bol_burrito', 'Bol burrito', 'Burrito bowl', 'lunch', 25, '🥙',
    [['Riz', 'Rice', ['1 tasse', '1 cup']], ['Haricots noirs', 'Black beans', ['1 boîte', '1 can']], ['Maïs', 'Corn', ['1 tasse', '1 cup']], ['Poulet', 'Chicken', '300 g'], ['Salsa', 'Salsa', ['½ tasse', '½ cup']]],
    ['Cuis le riz et le poulet.', 'Réchauffe haricots et maïs.', 'Assemble le bol, garnis de salsa.'],
    ['Cook the rice and chicken.', 'Warm beans and corn.', 'Build the bowl, top with salsa.'],
    { diet: ['dairyFree', 'glutenFree', 'highProtein'], nutrition: { cal: 520, p: 34, c: 64, f: 12 } }),
  qc('croque_monsieur', 'Croque-monsieur', 'Croque-monsieur', 'lunch', 15, '🥪',
    [['Pain', 'Bread', '4 tranches'], ['Jambon', 'Ham', '4 tranches'], ['Fromage suisse', 'Swiss cheese', '4 tranches'], ['Béchamel', 'Béchamel', ['½ tasse', '½ cup']]],
    ['Monte les sandwichs jambon-fromage.', 'Nappe de béchamel et fromage.', 'Gratine au four 8 min.'],
    ['Build the ham-cheese sandwiches.', 'Top with béchamel and cheese.', 'Broil 8 min.'],
    { allergens: ['gluten', 'dairy'], portions: 2, nutrition: { cal: 480, p: 26, c: 34, f: 26 } }),
  qc('soupe_poulet_nouilles', 'Soupe poulet et nouilles', 'Chicken noodle soup', 'lunch', 35, '🍜',
    [['Poulet cuit', 'Cooked chicken', ['2 tasses', '2 cups']], ['Nouilles aux œufs', 'Egg noodles', ['2 tasses', '2 cups']], ['Carottes', 'Carrots', '2'], ['Céleri', 'Celery', '2 branches'], ['Bouillon de poulet', 'Chicken broth', ['6 tasses', '6 cups']]],
    ['Fais revenir carottes et céleri.', 'Ajoute bouillon et poulet, mijote 15 min.', 'Ajoute les nouilles, cuis 8 min.'],
    ['Sauté carrots and celery.', 'Add broth and chicken, simmer 15 min.', 'Add noodles, cook 8 min.'],
    { allergens: ['gluten', 'eggs'], diet: ['highProtein'], nutrition: { cal: 300, p: 24, c: 32, f: 7 } }),
];

// ── 2e vague : plats du monde, soir de semaine, déjeuners, desserts ──
const QC2_RECIPES = [
  qc('butter_chicken', 'Poulet au beurre', 'Butter chicken', 'dinner', 40, '🍛',
    [['Poulet', 'Chicken', '600 g'], ['Sauce tomate', 'Tomato sauce', ['1 tasse', '1 cup']], ['Crème', 'Cream', ['½ tasse', '½ cup']], ['Garam masala', 'Garam masala', ['2 c. à soupe', '2 tbsp']], ['Ail', 'Garlic', ['3 gousses', '3 cloves']], ['Riz', 'Rice', ['2 tasses', '2 cups']]],
    ['Saisis le poulet épicé.', 'Ajoute tomate, crème et épices.', 'Mijote 20 min, sers sur riz.'],
    ['Sear the spiced chicken.', 'Add tomato, cream and spices.', 'Simmer 20 min, serve over rice.'],
    { allergens: ['dairy'], diet: ['glutenFree', 'highProtein'], tagFr: 'Classique indien', tagEn: 'Indian classic', nutrition: { cal: 620, p: 40, c: 56, f: 24 } }),
  qc('pad_thai', 'Pad thaï', 'Pad thai', 'dinner', 30, '🍜',
    [['Nouilles de riz', 'Rice noodles', '250 g'], ['Crevettes', 'Shrimp', '300 g'], ['Œufs', 'Eggs', '2'], ['Arachides', 'Peanuts', ['¼ tasse', '¼ cup']], ['Sauce de poisson', 'Fish sauce', ['3 c. à soupe', '3 tbsp']], ['Lime', 'Lime', '1']],
    ['Trempe les nouilles.', 'Saute crevettes, œufs et nouilles.', 'Ajoute sauce, arachides et lime.'],
    ['Soak the noodles.', 'Stir-fry shrimp, eggs and noodles.', 'Add sauce, peanuts and lime.'],
    { allergens: ['shellfish', 'eggs', 'peanuts', 'fish'], diet: ['dairyFree'], tagFr: 'Thaïlande', tagEn: 'Thai', nutrition: { cal: 540, p: 28, c: 62, f: 20 } }),
  qc('poke_bowl', 'Bol poké au thon', 'Tuna poke bowl', 'lunch', 20, '🍣',
    [['Thon cru', 'Sushi-grade tuna', '250 g'], ['Riz', 'Rice', ['1 ½ tasse', '1 ½ cup']], ['Avocat', 'Avocado', '1'], ['Edamame', 'Edamame', ['½ tasse', '½ cup']], ['Sauce soya', 'Soy sauce', ['3 c. à soupe', '3 tbsp']], ['Concombre', 'Cucumber', '1']],
    ['Cuis le riz, laisse tiédir.', 'Coupe thon, avocat et concombre.', 'Assemble le bol, arrose de soya.'],
    ['Cook the rice, let cool slightly.', 'Dice tuna, avocado and cucumber.', 'Build the bowl, drizzle with soy.'],
    { allergens: ['fish', 'soy'], diet: ['dairyFree', 'pescatarian', 'highProtein'], tagFr: 'Hawaï', tagEn: 'Hawaiian', nutrition: { cal: 520, p: 32, c: 58, f: 16 } }),
  qc('ramen', 'Ramen maison', 'Homemade ramen', 'dinner', 35, '🍜',
    [['Nouilles ramen', 'Ramen noodles', '2 portions'], ['Bouillon de poulet', 'Chicken broth', ['4 tasses', '4 cups']], ['Œufs', 'Eggs', '2'], ['Porc', 'Pork', '200 g'], ['Oignon vert', 'Green onion', '3'], ['Miso', 'Miso', ['2 c. à soupe', '2 tbsp']]],
    ['Chauffe le bouillon avec le miso.', 'Cuis les nouilles, mollets les œufs.', 'Monte le bol, garnis de porc et oignon vert.'],
    ['Heat broth with miso.', 'Cook noodles, soft-boil eggs.', 'Build the bowl, top with pork and scallion.'],
    { allergens: ['gluten', 'eggs', 'soy'], tagFr: 'Japon', tagEn: 'Japanese', nutrition: { cal: 560, p: 30, c: 58, f: 22 } }),
  qc('lasagne', 'Lasagne à la viande', 'Meat lasagna', 'dinner', 75, '🍝',
    [['Pâtes à lasagne', 'Lasagna noodles', '12'], ['Bœuf haché', 'Ground beef', '500 g'], ['Sauce tomate', 'Tomato sauce', ['3 tasses', '3 cups']], ['Ricotta', 'Ricotta', ['2 tasses', '2 cups']], ['Mozzarella', 'Mozzarella', ['2 tasses', '2 cups']]],
    ['Prépare la sauce à la viande.', 'Monte en couches pâtes, sauce, fromages.', 'Cuis 45 min à 375 °F.'],
    ['Make the meat sauce.', 'Layer noodles, sauce, cheeses.', 'Bake 45 min at 375 °F.'],
    { allergens: ['gluten', 'dairy'], portions: 6, tagFr: 'Italie', tagEn: 'Italian', nutrition: { cal: 580, p: 34, c: 48, f: 28 } }),
  qc('mac_cheese', 'Macaroni au fromage', 'Mac and cheese', 'dinner', 30, '🧀',
    [['Macaroni', 'Macaroni', '400 g'], ['Cheddar', 'Cheddar', ['2 tasses', '2 cups']], ['Lait', 'Milk', ['2 tasses', '2 cups']], ['Beurre', 'Butter', ['3 c. à soupe', '3 tbsp']], ['Farine', 'Flour', ['3 c. à soupe', '3 tbsp']]],
    ['Cuis les pâtes.', 'Fais un roux, ajoute lait et fromage.', 'Mélange aux pâtes, sers.'],
    ['Cook the pasta.', 'Make a roux, add milk and cheese.', 'Toss with pasta, serve.'],
    { allergens: ['gluten', 'dairy'], diet: ['vegetarian'], tagFr: 'Réconfort', tagEn: 'Comfort food', nutrition: { cal: 620, p: 24, c: 64, f: 30 } }),
  qc('risotto', 'Risotto aux champignons', 'Mushroom risotto', 'dinner', 40, '🍚',
    [['Riz arborio', 'Arborio rice', ['1 ½ tasse', '1 ½ cup']], ['Champignons', 'Mushrooms', ['3 tasses', '3 cups']], ['Bouillon', 'Broth', ['5 tasses', '5 cups']], ['Parmesan', 'Parmesan', ['¾ tasse', '¾ cup']], ['Vin blanc', 'White wine', ['½ tasse', '½ cup']]],
    ['Fais revenir riz et champignons.', 'Ajoute le bouillon une louche à la fois.', 'Termine au parmesan.'],
    ['Sauté rice and mushrooms.', 'Add broth one ladle at a time.', 'Finish with parmesan.'],
    { allergens: ['dairy'], diet: ['vegetarian', 'glutenFree'], tagFr: 'Italie', tagEn: 'Italian', nutrition: { cal: 480, p: 14, c: 72, f: 14 } }),
  qc('fajitas', 'Fajitas au poulet', 'Chicken fajitas', 'dinner', 25, '🌯',
    [['Poulet', 'Chicken', '500 g'], ['Poivrons', 'Bell peppers', '2'], ['Oignon', 'Onion', '1'], ['Tortillas', 'Tortillas', '8'], ['Épices fajita', 'Fajita seasoning', ['2 c. à soupe', '2 tbsp']]],
    ['Coupe poulet et légumes en lanières.', 'Saute avec les épices.', 'Sers dans les tortillas.'],
    ['Slice chicken and veggies into strips.', 'Stir-fry with the spices.', 'Serve in tortillas.'],
    { allergens: ['gluten'], diet: ['dairyFree', 'highProtein'], tagFr: 'Mexique', tagEn: 'Mexican', nutrition: { cal: 480, p: 34, c: 44, f: 16 } }),
  qc('shawarma', 'Shawarma au poulet', 'Chicken shawarma', 'dinner', 35, '🥙',
    [['Poulet', 'Chicken', '600 g'], ['Pita', 'Pita', '4'], ['Yogourt', 'Yogurt', ['½ tasse', '½ cup']], ['Ail', 'Garlic', ['2 gousses', '2 cloves']], ['Cumin', 'Cumin', ['1 c. à soupe', '1 tbsp']]],
    ['Marine le poulet aux épices.', 'Grille jusqu\'à doré.', 'Sers en pita avec sauce à l\'ail.'],
    ['Marinate chicken in spices.', 'Grill until golden.', 'Serve in pita with garlic sauce.'],
    { allergens: ['gluten', 'dairy'], diet: ['highProtein'], tagFr: 'Moyen-Orient', tagEn: 'Middle Eastern', nutrition: { cal: 520, p: 40, c: 42, f: 18 } }),
  qc('falafel', 'Falafels', 'Falafel', 'lunch', 30, '🧆',
    [['Pois chiches', 'Chickpeas', ['2 tasses', '2 cups']], ['Persil', 'Parsley', ['1 tasse', '1 cup']], ['Ail', 'Garlic', ['3 gousses', '3 cloves']], ['Cumin', 'Cumin', ['1 c. à soupe', '1 tbsp']], ['Farine', 'Flour', ['3 c. à soupe', '3 tbsp']]],
    ['Mixe pois chiches, herbes et épices.', 'Forme des boulettes.', 'Fris jusqu\'à dorées.'],
    ['Blend chickpeas, herbs and spices.', 'Shape into balls.', 'Fry until golden.'],
    { allergens: ['gluten'], diet: ['vegan', 'vegetarian', 'dairyFree', 'highProtein'], tagFr: 'Moyen-Orient', tagEn: 'Middle Eastern', nutrition: { cal: 360, p: 14, c: 48, f: 14 } }),
  qc('hummus', 'Houmous', 'Hummus', 'lunch', 10, '🫛',
    [['Pois chiches', 'Chickpeas', ['1 boîte', '1 can']], ['Tahini', 'Tahini', ['¼ tasse', '¼ cup']], ['Citron', 'Lemon', '1'], ['Ail', 'Garlic', ['1 gousse', '1 clove']], ['Huile d\'olive', 'Olive oil', ['3 c. à soupe', '3 tbsp']]],
    ['Mets tout au robot.', 'Mixe jusqu\'à crémeux.', 'Arrose d\'huile, sers.'],
    ['Put everything in a food processor.', 'Blend until creamy.', 'Drizzle with oil, serve.'],
    { allergens: ['sesame'], diet: ['vegan', 'vegetarian', 'dairyFree', 'glutenFree'], tagFr: 'Mezze', tagEn: 'Mezze', nutrition: { cal: 220, p: 8, c: 22, f: 12 } }),
  qc('curry_legumes', 'Curry de légumes', 'Vegetable curry', 'dinner', 30, '🍛',
    [['Patate douce', 'Sweet potato', '2'], ['Pois chiches', 'Chickpeas', ['1 boîte', '1 can']], ['Lait de coco', 'Coconut milk', ['1 boîte', '1 can']], ['Épinards', 'Spinach', ['2 tasses', '2 cups']], ['Pâte de cari', 'Curry paste', ['2 c. à soupe', '2 tbsp']]],
    ['Fais revenir la pâte de cari.', 'Ajoute patate, pois chiches, lait de coco.', 'Mijote 20 min, ajoute épinards.'],
    ['Fry the curry paste.', 'Add sweet potato, chickpeas, coconut milk.', 'Simmer 20 min, stir in spinach.'],
    { diet: ['vegan', 'vegetarian', 'dairyFree', 'glutenFree'], tagFr: 'Réconfort végé', tagEn: 'Veggie comfort', nutrition: { cal: 420, p: 12, c: 52, f: 20 } }),
  qc('soupe_tortilla', 'Soupe tortilla', 'Tortilla soup', 'lunch', 35, '🍲',
    [['Poulet', 'Chicken', '300 g'], ['Tomates', 'Tomatoes', ['1 boîte', '1 can']], ['Maïs', 'Corn', ['1 tasse', '1 cup']], ['Haricots noirs', 'Black beans', ['1 boîte', '1 can']], ['Tortillas', 'Tortillas', '4']],
    ['Mijote poulet, tomates, maïs, haricots.', 'Effiloche le poulet.', 'Garnis de lanières de tortilla croustillantes.'],
    ['Simmer chicken, tomatoes, corn, beans.', 'Shred the chicken.', 'Top with crispy tortilla strips.'],
    { allergens: ['gluten'], diet: ['dairyFree', 'highProtein'], tagFr: 'Mexique', tagEn: 'Mexican', nutrition: { cal: 380, p: 28, c: 44, f: 10 } }),
  qc('pancakes', 'Pancakes moelleux', 'Fluffy pancakes', 'breakfast', 20, '🥞',
    [['Farine', 'Flour', ['1 ½ tasse', '1 ½ cup']], ['Lait', 'Milk', ['1 ¼ tasse', '1 ¼ cup']], ['Œuf', 'Egg', '1'], ['Poudre à pâte', 'Baking powder', ['1 c. à soupe', '1 tbsp']], ['Sucre', 'Sugar', ['2 c. à soupe', '2 tbsp']]],
    ['Mélange secs et liquides.', 'Verse des ronds dans la poêle.', 'Retourne quand des bulles apparaissent.'],
    ['Mix dry and wet.', 'Pour rounds into the pan.', 'Flip when bubbles form.'],
    { allergens: ['gluten', 'eggs', 'dairy'], diet: ['vegetarian'], tagFr: 'Déjeuner', tagEn: 'Breakfast', nutrition: { cal: 280, p: 9, c: 44, f: 7 } }),
  qc('smoothie_bowl', 'Bol smoothie', 'Smoothie bowl', 'breakfast', 10, '🥣',
    [['Banane congelée', 'Frozen banana', '2'], ['Petits fruits', 'Mixed berries', ['1 tasse', '1 cup']], ['Yogourt grec', 'Greek yogurt', ['½ tasse', '½ cup']], ['Granola', 'Granola', ['½ tasse', '½ cup']]],
    ['Mixe bananes, fruits et yogourt épais.', 'Verse dans un bol.', 'Garnis de granola et fruits.'],
    ['Blend bananas, berries and yogurt thick.', 'Pour into a bowl.', 'Top with granola and fruit.'],
    { allergens: ['dairy', 'gluten'], diet: ['vegetarian', 'highProtein'], portions: 2, tagFr: 'Déjeuner', tagEn: 'Breakfast', nutrition: { cal: 340, p: 14, c: 58, f: 7 } }),
  qc('granola', 'Granola maison', 'Homemade granola', 'breakfast', 35, '🥣',
    [['Flocons d\'avoine', 'Rolled oats', ['3 tasses', '3 cups']], ['Noix', 'Nuts', ['1 tasse', '1 cup']], ['Miel', 'Honey', ['½ tasse', '½ cup']], ['Huile', 'Oil', ['¼ tasse', '¼ cup']], ['Cannelle', 'Cinnamon', ['1 c. à thé', '1 tsp']]],
    ['Mélange tous les ingrédients.', 'Étale sur une plaque.', 'Cuis 25 min à 325 °F en remuant.'],
    ['Mix all ingredients.', 'Spread on a sheet pan.', 'Bake 25 min at 325 °F, stirring.'],
    { allergens: ['gluten', 'nuts'], diet: ['vegetarian', 'dairyFree'], portions: 8, tagFr: 'Déjeuner', tagEn: 'Breakfast', nutrition: { cal: 320, p: 8, c: 42, f: 15 } }),
  qc('oeufs_benedictine', 'Œufs bénédictine', 'Eggs Benedict', 'breakfast', 25, '🥚',
    [['Œufs', 'Eggs', '4'], ['Muffins anglais', 'English muffins', '2'], ['Jambon', 'Ham', '4 tranches'], ['Sauce hollandaise', 'Hollandaise', ['1 tasse', '1 cup']]],
    ['Poche les œufs.', 'Grille les muffins, ajoute le jambon.', 'Dépose l\'œuf, nappe de hollandaise.'],
    ['Poach the eggs.', 'Toast the muffins, add ham.', 'Top with egg and hollandaise.'],
    { allergens: ['gluten', 'eggs', 'dairy'], tagFr: 'Brunch', tagEn: 'Brunch', nutrition: { cal: 480, p: 24, c: 28, f: 30 } }),
  qc('bagel_saumon', 'Bagel au saumon fumé', 'Smoked salmon bagel', 'breakfast', 10, '🥯',
    [['Bagel', 'Bagel', '2'], ['Saumon fumé', 'Smoked salmon', '120 g'], ['Fromage à la crème', 'Cream cheese', ['¼ tasse', '¼ cup']], ['Câpres', 'Capers', ['1 c. à soupe', '1 tbsp']], ['Oignon rouge', 'Red onion', '¼']],
    ['Grille les bagels.', 'Tartine de fromage à la crème.', 'Ajoute saumon, câpres, oignon.'],
    ['Toast the bagels.', 'Spread with cream cheese.', 'Add salmon, capers, onion.'],
    { allergens: ['gluten', 'dairy', 'fish'], diet: ['pescatarian'], portions: 2, tagFr: 'Brunch', tagEn: 'Brunch', nutrition: { cal: 420, p: 24, c: 46, f: 16 } }),
  qc('parfait_yogourt', 'Parfait au yogourt', 'Yogurt parfait', 'breakfast', 5, '🍨',
    [['Yogourt grec', 'Greek yogurt', ['1 tasse', '1 cup']], ['Granola', 'Granola', ['½ tasse', '½ cup']], ['Petits fruits', 'Berries', ['½ tasse', '½ cup']], ['Miel', 'Honey', ['1 c. à soupe', '1 tbsp']]],
    ['Alterne yogourt, granola et fruits.', 'Arrose de miel.', 'Sers immédiatement.'],
    ['Layer yogurt, granola and fruit.', 'Drizzle with honey.', 'Serve right away.'],
    { allergens: ['dairy', 'gluten'], diet: ['vegetarian', 'highProtein'], portions: 1, tagFr: 'Déjeuner', tagEn: 'Breakfast', nutrition: { cal: 300, p: 18, c: 44, f: 6 } }),
  qc('cossetarde', 'Crème brûlée', 'Crème brûlée', 'dinner', 50, '🍮',
    [['Crème 35 %', 'Heavy cream', ['2 tasses', '2 cups']], ['Jaunes d\'œufs', 'Egg yolks', '5'], ['Sucre', 'Sugar', ['½ tasse', '½ cup']], ['Vanille', 'Vanilla', ['1 c. à thé', '1 tsp']]],
    ['Chauffe la crème vanillée.', 'Fouette jaunes et sucre, tempère.', 'Cuis au bain-marie 35 min, caramélise.'],
    ['Heat the vanilla cream.', 'Whisk yolks and sugar, temper.', 'Bake in a water bath 35 min, torch the top.'],
    { allergens: ['eggs', 'dairy'], diet: ['vegetarian', 'glutenFree'], tagFr: 'Dessert chic', tagEn: 'Fancy dessert', nutrition: { cal: 420, p: 6, c: 32, f: 30 } }),
  qc('tiramisu', 'Tiramisu', 'Tiramisu', 'dinner', 30, '🍰',
    [['Mascarpone', 'Mascarpone', ['2 tasses', '2 cups']], ['Doigts de dame', 'Ladyfingers', '24'], ['Café fort', 'Strong coffee', ['1 tasse', '1 cup']], ['Œufs', 'Eggs', '3'], ['Cacao', 'Cocoa', ['2 c. à soupe', '2 tbsp']]],
    ['Fouette mascarpone, jaunes et sucre.', 'Trempe les biscuits dans le café.', 'Monte en couches, saupoudre de cacao.'],
    ['Whip mascarpone, yolks and sugar.', 'Dip the biscuits in coffee.', 'Layer up, dust with cocoa.'],
    { allergens: ['gluten', 'eggs', 'dairy'], diet: ['vegetarian'], portions: 8, tagFr: 'Italie', tagEn: 'Italian', nutrition: { cal: 380, p: 8, c: 34, f: 24 } }),
  qc('mousse_chocolat', 'Mousse au chocolat', 'Chocolate mousse', 'dinner', 20, '🍫',
    [['Chocolat noir', 'Dark chocolate', '200 g'], ['Œufs', 'Eggs', '4'], ['Sucre', 'Sugar', ['¼ tasse', '¼ cup']], ['Crème', 'Cream', ['½ tasse', '½ cup']]],
    ['Fais fondre le chocolat.', 'Incorpore jaunes, puis blancs montés.', 'Réfrigère 3 h.'],
    ['Melt the chocolate.', 'Fold in yolks, then whipped whites.', 'Chill 3 h.'],
    { allergens: ['eggs', 'dairy'], diet: ['vegetarian', 'glutenFree'], tagFr: 'Dessert', tagEn: 'Dessert', nutrition: { cal: 340, p: 7, c: 28, f: 23 } }),
  qc('cretes_erable', 'Galette de sarrasin', 'Buckwheat galette', 'breakfast', 25, '🥞',
    [['Farine de sarrasin', 'Buckwheat flour', ['1 tasse', '1 cup']], ['Eau', 'Water', ['1 ½ tasse', '1 ½ cup']], ['Sel', 'Salt', ['1 pincée', '1 pinch']], ['Sirop d\'érable', 'Maple syrup', ['au goût', 'to taste']]],
    ['Mélange farine, eau et sel.', 'Cuis de fines galettes.', 'Sers avec sirop d\'érable.'],
    ['Mix flour, water and salt.', 'Cook thin galettes.', 'Serve with maple syrup.'],
    { diet: ['vegan', 'vegetarian', 'dairyFree'], tagFr: 'Tradition', tagEn: 'Traditional', nutrition: { cal: 210, p: 5, c: 44, f: 2 } }),
  qc('soupe_miso', 'Soupe miso', 'Miso soup', 'lunch', 15, '🍵',
    [['Miso', 'Miso', ['3 c. à soupe', '3 tbsp']], ['Tofu', 'Tofu', '200 g'], ['Algues wakame', 'Wakame', ['2 c. à soupe', '2 tbsp']], ['Oignon vert', 'Green onion', '2']],
    ['Chauffe l\'eau sans bouillir.', 'Délaye le miso.', 'Ajoute tofu, algues, oignon vert.'],
    ['Heat water without boiling.', 'Whisk in the miso.', 'Add tofu, seaweed, scallion.'],
    { allergens: ['soy'], diet: ['vegan', 'vegetarian', 'dairyFree', 'glutenFree'], tagFr: 'Japon', tagEn: 'Japanese', nutrition: { cal: 120, p: 9, c: 8, f: 5 } }),
  qc('bibimbap', 'Bibimbap', 'Bibimbap', 'dinner', 35, '🍚',
    [['Riz', 'Rice', ['2 tasses', '2 cups']], ['Bœuf', 'Beef', '300 g'], ['Épinards', 'Spinach', ['2 tasses', '2 cups']], ['Carottes', 'Carrots', '2'], ['Œufs', 'Eggs', '2'], ['Gochujang', 'Gochujang', ['2 c. à soupe', '2 tbsp']]],
    ['Cuis riz, bœuf et légumes séparément.', 'Dispose en sections sur le riz.', 'Ajoute l\'œuf et le gochujang.'],
    ['Cook rice, beef and veggies separately.', 'Arrange in sections over rice.', 'Top with egg and gochujang.'],
    { allergens: ['eggs', 'soy'], diet: ['dairyFree', 'highProtein'], tagFr: 'Corée', tagEn: 'Korean', nutrition: { cal: 560, p: 32, c: 62, f: 18 } }),
  qc('gnocchi', 'Gnocchis sauce rosée', 'Gnocchi rosé', 'dinner', 25, '🥟',
    [['Gnocchis', 'Gnocchi', '500 g'], ['Sauce tomate', 'Tomato sauce', ['1 ½ tasse', '1 ½ cup']], ['Crème', 'Cream', ['½ tasse', '½ cup']], ['Parmesan', 'Parmesan', ['½ tasse', '½ cup']]],
    ['Cuis les gnocchis.', 'Mélange sauce tomate et crème.', 'Nappe, ajoute parmesan.'],
    ['Cook the gnocchi.', 'Mix tomato sauce and cream.', 'Coat, add parmesan.'],
    { allergens: ['gluten', 'dairy'], diet: ['vegetarian'], tagFr: 'Italie', tagEn: 'Italian', nutrition: { cal: 480, p: 14, c: 68, f: 16 } }),
  qc('soupe_nouilles_boeuf', 'Soupe-repas au bœuf', 'Beef noodle soup', 'lunch', 30, '🍜',
    [['Bœuf', 'Beef', '300 g'], ['Nouilles', 'Noodles', '200 g'], ['Bouillon', 'Broth', ['6 tasses', '6 cups']], ['Bok choy', 'Bok choy', '2'], ['Gingembre', 'Ginger', ['1 c. à soupe', '1 tbsp']]],
    ['Parfume le bouillon au gingembre.', 'Cuis le bœuf tranché et les nouilles.', 'Ajoute le bok choy, sers.'],
    ['Infuse broth with ginger.', 'Cook sliced beef and noodles.', 'Add bok choy, serve.'],
    { allergens: ['gluten'], diet: ['dairyFree', 'highProtein'], tagFr: 'Asie', tagEn: 'Asian', nutrition: { cal: 440, p: 30, c: 46, f: 14 } }),
  qc('wrap_poulet', 'Wrap au poulet César', 'Chicken Caesar wrap', 'lunch', 15, '🌯',
    [['Tortilla', 'Tortilla', '2'], ['Poulet grillé', 'Grilled chicken', '250 g'], ['Laitue romaine', 'Romaine', ['2 tasses', '2 cups']], ['Vinaigrette César', 'Caesar dressing', ['¼ tasse', '¼ cup']], ['Parmesan', 'Parmesan', ['¼ tasse', '¼ cup']]],
    ['Mélange poulet, laitue et vinaigrette.', 'Garnis les tortillas.', 'Roule serré, coupe en deux.'],
    ['Toss chicken, lettuce and dressing.', 'Fill the tortillas.', 'Roll tightly, cut in half.'],
    { allergens: ['gluten', 'dairy', 'eggs', 'fish'], diet: ['highProtein'], portions: 2, tagFr: 'Lunch rapide', tagEn: 'Quick lunch', nutrition: { cal: 460, p: 32, c: 36, f: 20 } }),
  qc('chaudree_mais', 'Chaudrée de maïs', 'Corn chowder', 'lunch', 35, '🌽',
    [['Maïs', 'Corn', ['3 tasses', '3 cups']], ['Pommes de terre', 'Potatoes', '3'], ['Bacon', 'Bacon', '4 tranches'], ['Crème', 'Cream', ['1 tasse', '1 cup']], ['Oignon', 'Onion', '1']],
    ['Cuis le bacon, réserve.', 'Mijote pommes de terre et maïs.', 'Ajoute crème et bacon, sers.'],
    ['Cook bacon, set aside.', 'Simmer potatoes and corn.', 'Add cream and bacon, serve.'],
    { allergens: ['dairy'], diet: ['glutenFree'], tagFr: 'Réconfort', tagEn: 'Comfort', nutrition: { cal: 380, p: 12, c: 44, f: 18 } }),
  qc('crepe_jambon', 'Crêpe jambon-fromage', 'Ham & cheese crêpe', 'lunch', 20, '🫓',
    [['Crêpes', 'Crêpes', '4'], ['Jambon', 'Ham', '4 tranches'], ['Fromage suisse', 'Swiss cheese', ['1 tasse', '1 cup']], ['Œufs', 'Eggs', '4']],
    ['Réchauffe les crêpes.', 'Garnis de jambon, fromage et œuf.', 'Plie et gratine.'],
    ['Warm the crêpes.', 'Fill with ham, cheese and egg.', 'Fold and broil.'],
    { allergens: ['gluten', 'eggs', 'dairy'], tagFr: 'Bretagne', tagEn: 'Breton', nutrition: { cal: 420, p: 24, c: 28, f: 22 } }),
  qc('tofu_general', 'Tofu Général Tao', 'General Tao tofu', 'dinner', 30, '🍢',
    [['Tofu ferme', 'Firm tofu', '400 g'], ['Fécule de maïs', 'Cornstarch', ['½ tasse', '½ cup']], ['Sauce Général Tao', 'General Tao sauce', ['¾ tasse', '¾ cup']], ['Riz', 'Rice', ['2 tasses', '2 cups']], ['Brocoli', 'Broccoli', ['2 tasses', '2 cups']]],
    ['Enrobe le tofu de fécule, fris.', 'Enrobe de sauce.', 'Sers sur riz avec brocoli.'],
    ['Coat tofu in cornstarch, fry.', 'Toss in sauce.', 'Serve over rice with broccoli.'],
    { allergens: ['soy', 'gluten'], diet: ['vegan', 'vegetarian', 'dairyFree'], tagFr: 'Végé', tagEn: 'Veggie', nutrition: { cal: 520, p: 20, c: 78, f: 14 } }),
  qc('salade_quinoa', 'Salade de quinoa', 'Quinoa salad', 'lunch', 25, '🥗',
    [['Quinoa', 'Quinoa', ['1 tasse', '1 cup']], ['Concombre', 'Cucumber', '1'], ['Tomates cerises', 'Cherry tomatoes', ['1 tasse', '1 cup']], ['Féta', 'Feta', ['½ tasse', '½ cup']], ['Citron', 'Lemon', '1']],
    ['Cuis et refroidis le quinoa.', 'Coupe les légumes.', 'Mélange avec féta, citron et huile.'],
    ['Cook and cool the quinoa.', 'Dice the vegetables.', 'Toss with feta, lemon and oil.'],
    { allergens: ['dairy'], diet: ['vegetarian', 'glutenFree', 'highProtein'], tagFr: 'Santé', tagEn: 'Healthy', nutrition: { cal: 360, p: 14, c: 42, f: 16 } }),
  qc('pizza_pochette', 'Pochette pizza', 'Pizza pocket', 'lunch', 25, '🥟',
    [['Pâte à pizza', 'Pizza dough', '1'], ['Sauce tomate', 'Tomato sauce', ['½ tasse', '½ cup']], ['Mozzarella', 'Mozzarella', ['1 tasse', '1 cup']], ['Pepperoni', 'Pepperoni', '20 tranches']],
    ['Étale la pâte, garnis une moitié.', 'Replie et scelle les bords.', 'Cuis 18 min à 425 °F.'],
    ['Roll the dough, fill one half.', 'Fold over and seal edges.', 'Bake 18 min at 425 °F.'],
    { allergens: ['gluten', 'dairy'], tagFr: 'Lunch', tagEn: 'Lunch', nutrition: { cal: 480, p: 20, c: 52, f: 22 } }),
  qc('cari_pois_chiches_epinards', 'Dahl de lentilles', 'Lentil dahl', 'dinner', 35, '🍛',
    [['Lentilles corail', 'Red lentils', ['1 ½ tasse', '1 ½ cup']], ['Lait de coco', 'Coconut milk', ['1 boîte', '1 can']], ['Tomates', 'Tomatoes', ['1 boîte', '1 can']], ['Cumin', 'Cumin', ['1 c. à soupe', '1 tbsp']], ['Gingembre', 'Ginger', ['1 c. à soupe', '1 tbsp']]],
    ['Fais revenir épices et gingembre.', 'Ajoute lentilles, tomates, lait de coco.', 'Mijote 25 min.'],
    ['Fry spices and ginger.', 'Add lentils, tomatoes, coconut milk.', 'Simmer 25 min.'],
    { diet: ['vegan', 'vegetarian', 'dairyFree', 'glutenFree', 'highProtein'], tagFr: 'Inde', tagEn: 'Indian', nutrition: { cal: 400, p: 18, c: 50, f: 16 } }),
  qc('steak_frites', 'Steak frites', 'Steak frites', 'dinner', 30, '🥩',
    [['Bifteck', 'Steak', '2'], ['Pommes de terre', 'Potatoes', '4'], ['Beurre', 'Butter', ['2 c. à soupe', '2 tbsp']], ['Ail', 'Garlic', ['2 gousses', '2 cloves']]],
    ['Fais les frites au four.', 'Poêle les steaks à feu vif.', 'Repos, beurre à l\'ail, sers.'],
    ['Bake the fries.', 'Sear the steaks on high heat.', 'Rest, garlic butter, serve.'],
    { allergens: ['dairy'], diet: ['glutenFree', 'highProtein'], portions: 2, tagFr: 'Bistro', tagEn: 'Bistro', nutrition: { cal: 680, p: 44, c: 42, f: 38 } }),
  qc('soupe_brocoli', 'Crème de brocoli', 'Cream of broccoli', 'lunch', 30, '🥦',
    [['Brocoli', 'Broccoli', '1'], ['Pommes de terre', 'Potatoes', '2'], ['Bouillon', 'Broth', ['4 tasses', '4 cups']], ['Crème', 'Cream', ['½ tasse', '½ cup']], ['Oignon', 'Onion', '1']],
    ['Mijote brocoli, patates, oignon.', 'Réduis en purée.', 'Ajoute la crème, sers.'],
    ['Simmer broccoli, potatoes, onion.', 'Blend smooth.', 'Stir in cream, serve.'],
    { allergens: ['dairy'], diet: ['vegetarian', 'glutenFree'], tagFr: 'Réconfort', tagEn: 'Comfort', nutrition: { cal: 240, p: 8, c: 28, f: 12 } }),
  qc('cipate', 'Pâté au saumon', 'Salmon pie', 'dinner', 50, '🥧',
    [['Saumon en conserve', 'Canned salmon', ['2 boîtes', '2 cans']], ['Pommes de terre', 'Potatoes', '4'], ['Pâte à tarte', 'Pie crust', '2'], ['Petits pois', 'Peas', ['1 tasse', '1 cup']]],
    ['Écrase patates et saumon.', 'Garnis la pâte, ajoute les pois.', 'Couvre, cuis 35 min à 375 °F.'],
    ['Mash potatoes and salmon.', 'Fill the crust, add peas.', 'Cover, bake 35 min at 375 °F.'],
    { allergens: ['gluten', 'fish'], diet: ['pescatarian'], tagFr: 'Classique québécois', tagEn: 'Québec classic', nutrition: { cal: 520, p: 26, c: 44, f: 28 } }),
  qc('pommes_frites_four', 'Bol bouddha', 'Buddha bowl', 'lunch', 30, '🥗',
    [['Quinoa', 'Quinoa', ['1 tasse', '1 cup']], ['Patate douce', 'Sweet potato', '1'], ['Pois chiches', 'Chickpeas', ['1 boîte', '1 can']], ['Avocat', 'Avocado', '1'], ['Chou kale', 'Kale', ['2 tasses', '2 cups']]],
    ['Rôtis patate douce et pois chiches.', 'Cuis le quinoa.', 'Assemble avec kale et avocat.'],
    ['Roast sweet potato and chickpeas.', 'Cook the quinoa.', 'Assemble with kale and avocado.'],
    { diet: ['vegan', 'vegetarian', 'dairyFree', 'glutenFree', 'highProtein'], tagFr: 'Santé', tagEn: 'Healthy', nutrition: { cal: 480, p: 16, c: 64, f: 18 } }),
];

window.EXTRA_RECIPES = EXTRA_RECIPES.concat(QC_RECIPES, QC2_RECIPES);
window.RECIPE_DIETS = DIETS;
window.RECIPE_ALLERGENS = ALLERGENS;
window.MealDB = MealDB;
window.scaleQty = scaleQty;
window.parseRecipeSteps = parseSteps;
window.matchRecipe = matchRecipe;
window.inferAllergens = inferAllergens;
