import { useState, useEffect, useCallback } from "react"; // ─── Bread Profiles ─────────────────────────────────────────────────────────── const BREADS = { white: { id: "white", name: "Classic White", emoji: "🍞", tagline: "Light, airy crumb with a crisp crust", color: "#C8903A", defaultHydration: 75, hydrationRange: [68, 82], defaultLoafSize: "standard", flours: [{ name: "Bread flour", pct: 100, id: "bf" }], starterPct: 20, saltPct: 2, notes: "The workhorse loaf. High-protein bread flour gives excellent gluten structure and open crumb. Great for sandwiches, toast, and everything in between.", tips: [ "Use bread flour (12–13% protein) for best oven spring", "At 75% hydration this is very manageable — push to 80% for a more open crumb once confident", "Longer cold retard (16–18 hrs) deepens flavor significantly", ], steps: [ { title: "Autolyse", time: "30–60 min", body: "Mix all flour with 90% of the water. Stir until no dry flour remains. Cover and rest. This passive rest builds gluten without kneading." }, { title: "Add Starter & Salt", time: "5 min", body: "Add starter and salt with remaining water. Pinch and fold until fully incorporated, 3–5 min." }, { title: "Bulk Fermentation", time: "4–6 hrs @ 75°F", body: "Perform 4 sets of stretch & folds in the first 2 hours, spaced 30 min apart. Leave undisturbed after. Look for 50–75% volume increase, visible bubbles, and a jiggly, airy feel." }, { title: "Pre-Shape & Bench Rest", time: "25 min", body: "Turn dough onto unfloured surface, fold edges to center, flip seam-down, and drag with a bench scraper to build tension. Rest uncovered 20–25 min." }, { title: "Final Shape", time: "5 min", body: "Shape into a boule or batard. Place seam-UP into a well-floured banneton. At higher hydrations, use plenty of rice flour." }, { title: "Proof", time: "See timing tab", body: "Cover and proof per your schedule. Poke test: a floured finger should spring back slowly, about halfway." }, { title: "Bake (Covered)", time: "20 min @ 500°F", body: "Preheat Dutch oven 45+ min. Score at 30–45°, bake covered at 500°F for 20 min." }, { title: "Bake (Uncovered)", time: "20–25 min @ 450°F", body: "Remove lid, reduce to 450°F. Bake until deep mahogany. Internal temp 205–210°F." }, { title: "Cool", time: "1+ hr", body: "Rest on a wire rack at least 1 hour before cutting. The crumb sets as steam escapes." }, ], }, wheat: { id: "wheat", name: "Whole Wheat", emoji: "🌾", tagline: "Nutty, hearty, complex — the everyday loaf", color: "#8B6914", defaultHydration: 78, hydrationRange: [72, 85], defaultLoafSize: "standard", flours: [{ name: "Bread flour", pct: 70, id: "bf" }, { name: "Whole wheat flour", pct: 30, id: "ww" }], starterPct: 20, saltPct: 2, notes: "30% whole wheat adds nutty depth and earthy flavor. Whole wheat absorbs more water and ferments faster — watch your bulk closely. Higher hydration compensates for the bran.", tips: [ "Whole wheat ferments faster than white — reduce bulk time by 30–60 min at the same temp", "Bran cuts gluten strands; more than 40% whole wheat needs careful shaping", "Substitute up to 10% with rye for an even deeper, more complex flavor", ], steps: [ { title: "Autolyse", time: "45–60 min", body: "Mix all flours with 90% of the water. Whole wheat needs longer autolyse — go the full 60 min. The bran hydrates and gluten develops without effort." }, { title: "Add Starter & Salt", time: "5 min", body: "Add starter and salt with remaining water. Pinch and fold until incorporated. The dough will feel slightly tacky — that's normal at this hydration." }, { title: "Bulk Fermentation", time: "3.5–5 hrs @ 75°F", body: "4 sets of stretch & folds in the first 2 hours. Whole wheat ferments faster — start checking at 3.5 hours. Look for 50–60% rise (whole wheat rises less dramatically than white)." }, { title: "Pre-Shape & Bench Rest", time: "20 min", body: "Gentle pre-shape — whole wheat dough is more fragile. Build tension without degassing. Rest 20 min." }, { title: "Final Shape", time: "5 min", body: "Shape confidently but gently. The dough won't hold as tightly as pure white — that's fine. Rice flour heavily in the banneton." }, { title: "Proof", time: "See timing tab", body: "Cold retard is especially good for whole wheat — extra flavor development and easier scoring." }, { title: "Bake (Covered)", time: "20 min @ 500°F", body: "Score decisively — whole wheat doughs can be sluggish to open. A steep, confident score works better than a shallow one." }, { title: "Bake (Uncovered)", time: "22–27 min @ 450°F", body: "Whole wheat darkens faster — start checking at 20 min. Internal temp 205–208°F." }, { title: "Cool", time: "1.5+ hr", body: "Whole wheat benefits from a longer cool — 1.5 hours minimum for the crumb to fully set." }, ], }, rye: { id: "rye", name: "Dark Rye", emoji: "🖤", tagline: "Dense, tangy, deeply flavored — a serious loaf", color: "#7A4F3A", defaultHydration: 80, hydrationRange: [75, 88], defaultLoafSize: "standard", flours: [{ name: "Bread flour", pct: 60, id: "bf" }, { name: "Dark rye flour", pct: 40, id: "rye" }], starterPct: 25, saltPct: 2, notes: "40% rye creates a dense, moist crumb with strong sour notes. Rye has very little gluten-forming protein — the dough behaves more like clay than elastic bread dough. Different technique, exceptional results.", tips: [ "Rye dough doesn't stretch like wheat — mix well and skip traditional stretch & fold", "Rye ferments aggressively — keep temps at 72–74°F and watch closely", "Skip autolyse — rye benefits from immediate mixing with starter", "A loaf pan is recommended at 40%+ rye; it won't hold its shape in a Dutch oven", ], steps: [ { title: "Mix (No Autolyse)", time: "5 min", body: "Combine all flours, water, starter, and salt at once. Mix vigorously until fully combined — the dough will be sticky and clay-like, not stretchy. This is correct." }, { title: "Bulk Fermentation", time: "3–4 hrs @ 72°F", body: "Rye ferments fast. Do 2 sets of 'fold and turn' in the first hour. Judge by 40–50% rise and a network of small bubbles — not a windowpane test." }, { title: "Shape (Loaf Pan)", time: "5 min", body: "With wet hands, transfer dough to a well-oiled loaf pan. Smooth the top. Dust with rye flour or seeds." }, { title: "Final Proof", time: "1–2 hrs room temp OR overnight cold", body: "Dough should dome about ½ inch above the pan rim. Rye proofs quickly — watch carefully. Cold retard (8–12 hrs) improves flavor." }, { title: "Bake (Covered with foil)", time: "20 min @ 450°F", body: "Cover loosely with foil. Bake in the loaf pan at 450°F." }, { title: "Bake (Uncovered)", time: "25–35 min @ 400°F", body: "Remove foil, reduce to 400°F. Bake until deep brown, internal temp 205–210°F. Rye often looks done before it is — use a thermometer." }, { title: "Cool (Critical)", time: "2+ hrs", body: "Rye crumb is almost gummy when hot. Wait at least 2 hours, ideally overnight. Cutting too soon is the #1 rye mistake." }, ], }, seeded: { id: "seeded", name: "Seeded Multigrain", emoji: "🌻", tagline: "Toasty, crunchy, packed with texture", color: "#6B7C2E", defaultHydration: 76, hydrationRange: [70, 80], defaultLoafSize: "standard", flours: [{ name: "Bread flour", pct: 80, id: "bf" }, { name: "Whole wheat flour", pct: 10, id: "ww" }, { name: "Rolled oats", pct: 10, id: "oats" }], starterPct: 20, saltPct: 2, notes: "A toasted seed soaker adds incredible crunch and nutty depth without disrupting gluten structure. Toast the seeds first — it makes a dramatic difference in flavor.", tips: [ "Toast seeds before soaking — it intensifies flavor dramatically", "Add soaker during pre-shape, not during bulk, to preserve gluten structure", "Great seed mixes: sunflower + sesame + flax; pepitas + poppy; everything bagel blend", ], steps: [ { title: "Make Seed Soaker (Night Before)", time: "Overnight", body: "Toast sunflower, sesame, and flax seeds in a dry pan. Mix with equal weight boiling water. Cover and refrigerate overnight. This prevents seeds from robbing moisture from the dough." }, { title: "Autolyse", time: "30–45 min", body: "Mix bread flour and whole wheat with 90% of the water. Rest 30–45 min." }, { title: "Add Starter, Salt & Oats", time: "5 min", body: "Add starter and salt with remaining water. Pinch and fold until combined. Add rolled oats and incorporate fully." }, { title: "Bulk Fermentation", time: "4–5.5 hrs @ 75°F", body: "4 sets of stretch & folds in first 2 hours. Look for 50–65% rise." }, { title: "Add Soaker at Pre-Shape", time: "5 min", body: "Drain excess liquid from soaker. Spread dough on surface, scatter seeds across it, then fold and work them in. Pre-shape and rest 25 min." }, { title: "Final Shape & Seed Crust", time: "5 min", body: "Shape normally. Before placing in banneton, roll the outside of the loaf across mixed seeds to coat the surface." }, { title: "Proof", time: "See timing tab", body: "Cold retard recommended — seeds can accelerate fermentation slightly." }, { title: "Bake (Covered)", time: "20 min @ 500°F", body: "Score through the seed crust with extra pressure. The seeds will toast beautifully in the steam." }, { title: "Bake (Uncovered)", time: "20–25 min @ 450°F", body: "Seeds on the crust will darken — that's color, not burning. Internal temp 205–210°F." }, { title: "Cool", time: "1+ hr", body: "The crust crackles loudly as it cools — that's the sign of a good loaf." }, ], }, enriched: { id: "enriched", name: "Soft Enriched", emoji: "🧈", tagline: "Tender, pillowy crumb — butter & milk magic", color: "#B07040", defaultHydration: 72, hydrationRange: [65, 78], defaultLoafSize: "standard", flours: [{ name: "All-purpose flour", pct: 100, id: "ap" }], starterPct: 15, saltPct: 1.8, notes: "Enriched with butter, milk, and honey for a soft, tender crumb. Excellent for sandwich loaves, dinner rolls, and toast. Ferments more slowly due to the enrichments.", tips: [ "Add softened (not melted) butter after the first 2 stretch & folds", "Use whole milk at room temperature — cold milk slows fermentation significantly", "Can be baked in a Pullman pan for a perfectly square sandwich loaf", ], steps: [ { title: "Mix Dough", time: "10 min", body: "Mix flour, milk (in place of most water), honey, starter, and salt. Knead until cohesive. No autolyse needed for enriched dough." }, { title: "Incorporate Butter", time: "5–8 min", body: "Add softened butter in 3–4 small pieces, working each in fully before the next. The dough looks broken at first — keep working. It comes together into a smooth, glossy dough." }, { title: "Bulk Fermentation", time: "5–8 hrs @ 75°F", body: "Enriched doughs ferment slowly. Do 3 sets of stretch & folds in the first 90 min. Rise 50–60%. Be patient — don't rush with heat." }, { title: "Pre-Shape & Bench Rest", time: "20 min", body: "Pre-shape gently — enriched doughs are soft and extensible. Build tension without tearing. Rest 20 min uncovered." }, { title: "Final Shape", time: "5 min", body: "Shape as a batard for a loaf pan, or a tight boule for a Dutch oven. Seam-side UP in a floured banneton, or seam-side DOWN in a buttered loaf pan." }, { title: "Proof", time: "4–6 hrs room temp OR overnight cold", body: "Cold retard is highly recommended for flavor. Remove from fridge 30 min before baking (unlike lean doughs)." }, { title: "Bake (Covered)", time: "18 min @ 475°F", body: "Score with scissors for a rustic pull-apart look, or a single slash for a classic ear. Cover and bake at 475°F." }, { title: "Bake (Uncovered)", time: "15–20 min @ 425°F", body: "Enriched loaves brown faster due to sugar — watch carefully. Internal temp 190–195°F (lower than lean doughs)." }, { title: "Cool", time: "45 min", body: "45 min on a rack before cutting. The crust will soften as it cools — that's intentional." }, ], }, noknead: { id: "noknead", name: "No-Knead Overnight", emoji: "🌙", tagline: "Mix before bed, bake in the morning", color: "#4A6FA5", defaultHydration: 76, hydrationRange: [72, 82], defaultLoafSize: "standard", flours: [{ name: "Bread flour", pct: 100, id: "bf" }], starterPct: 10, saltPct: 2, notes: "Time does all the work here. A lower starter percentage and long overnight bulk means you mix once before bed and bake the next morning with zero stretch & fold. Total hands-on time is under 10 minutes. Inspired by Jim Lahey's no-knead method adapted for sourdough.", tips: [ "Lower starter % (10%) is intentional — it slows fermentation to fit an overnight schedule without over-proofing", "Cooler kitchens (65–68°F) are your friend here; warmer rooms may over-ferment by morning", "No banneton needed — the Dutch oven provides all the structure", "The dough will look shaggy and rough when you mix it. That's fine. Don't second-guess it.", ], steps: [ { title: "Mix (Night Before)", time: "5 min", body: "In a large bowl, combine all flour, water, starter, and salt. Mix with a fork or your hand until no dry flour remains — about 1–2 minutes. The dough will be rough and sticky. That's exactly right. Do not knead. Cover tightly with plastic wrap or a lid." }, { title: "Overnight Bulk Fermentation", time: "8–12 hrs @ 65–70°F", body: "Leave on the counter overnight. At 68°F this takes about 10–12 hrs. At 72°F, closer to 8–9 hrs. In the morning, the dough should have grown 50–75%, look bubbly and slightly domed on top, and smell pleasantly tangy. If your kitchen is warmer than 72°F, reduce starter to 8% or use cold water when mixing." }, { title: "Morning Fold", time: "2 min + 30 min rest", body: "With wet hands, perform 4 gentle folds directly in the bowl (one per side). Cover and let rest 30 minutes. This is the only 'work' you do — it slightly tightens the dough for better oven spring." }, { title: "Shape (Minimal)", time: "2 min", body: "Flour a surface lightly. Turn dough out and fold the edges to the center a few times to create a rough ball — no pre-shape bench rest needed. Flip seam-side down. It doesn't need to be perfect; the Dutch oven will hold it." }, { title: "Preheat Oven", time: "45 min", body: "Place Dutch oven with lid in oven and preheat to 500°F for at least 45 minutes while the shaped dough rests loosely covered on the counter. The long preheat is important — the Dutch oven must be fully saturated with heat." }, { title: "Score & Bake (Covered)", time: "20 min @ 500°F", body: "Carefully transfer dough to a sheet of parchment paper. Score with one confident slash. Lower into the screaming-hot Dutch oven using the parchment as a sling. Cover immediately and bake 20 minutes." }, { title: "Bake (Uncovered)", time: "20–25 min @ 450°F", body: "Remove the lid, drop heat to 450°F, and bake until deeply browned. Internal temp 205–210°F. The crust should be audibly crackly when you tap it." }, { title: "Cool", time: "1+ hr", body: "Transfer to a wire rack and wait at least 1 hour before cutting. The long overnight fermentation creates a very moist, open crumb — it needs time to set." }, ], }, fourgrain: { id: "fourgrain", name: "Four-Grain Hearty", emoji: "🥜", tagline: "Bran, oats & seeds — chewy and nutty", color: "#8C5A33", defaultHydration: 80, hydrationRange: [76, 86], defaultLoafSize: "standard", flours: [ { name: "Bread flour", pct: 71, id: "bf" }, { name: "Whole wheat flour", pct: 20, id: "ww" }, { name: "Wheat bran", pct: 6, id: "bran" }, { name: "Vital wheat gluten", pct: 3, id: "vwg" }, ], starterPct: 20, saltPct: 2.2, notes: "Four grains in one loaf: bread flour for structure, whole wheat for depth, wheat bran for fiber and texture, and steel cut oats for substantial chew. The oats AND bran soak together in all the dough's water, then the whole cooled mass goes straight in — no draining. Vital wheat gluten compensates for the bran and oats weakening the gluten network; honey balances the earthy bitterness; toasted sesame crusts it beautifully.", tips: [ "Soak the oats + bran together in all the water 2+ hrs (or overnight) — the bran soak softens its sharp edges so it does less damage to the gluten", "Cool the soaker to lukewarm (~90–100°F) before mixing, or the heat will kill your starter", "The whole soaker goes in — no draining. The water is calibrated to cover what the oats and bran absorb", "Judge doneness by feel — this heavy dough rises ~50%, not the dramatic doubling of white bread", ], // soaker water replaces the main dough water entirely (see soakerReplacesWater flag) soakerReplacesWater: true, extras: { oats: 0.12, // steel cut oats (vs total flour) honey: 0.04, sesame: 0.06, }, steps: [ { title: "Make Soaker (First)", time: "2+ hrs / overnight", body: "Combine steel cut oats and wheat bran in a heatproof bowl. Pour all the boiling water over them, stir, cover, and soak at least 2 hours (or overnight in the fridge) until the oats are tender and the bran softened. Cool to lukewarm (~90–100°F) before using — check the whole mass, not just the surface. Hot water kills starter." }, { title: "Toast Sesame Seeds", time: "5 min", body: "Toast sesame seeds in a dry skillet over medium heat, shaking often, until fragrant and golden, 3–5 min. Cool before using." }, { title: "Autolyse", time: "30–45 min", body: "Whisk bread flour, whole wheat, and vital wheat gluten together. Add the ENTIRE cooled soaker — oats, bran, and all the liquid — and stir until no dry flour remains. Cover and rest 30–45 min. The bran is already softened from its soak." }, { title: "Add Starter, Honey & Salt", time: "5 min", body: "Add starter, honey, and salt. Pinch and fold until incorporated, 4–5 min. It'll feel slack at first from the honey — keep working until cohesive." }, { title: "Bulk + Add Sesame", time: "5–6 hrs @ 75°F", body: "4 sets of stretch & folds in the first 2 hours. During the SECOND fold, work in most of the toasted sesame (reserve a spoonful for the crust). Leave undisturbed after fold 4. Ready at ~50% rise with a domed, bubbly top." }, { title: "Pre-Shape", time: "25 min", body: "Turn onto a lightly floured surface. Fold edges to center, flip seam-down, drag to build tension. Rest uncovered 25 min." }, { title: "Final Shape & Seed Crust", time: "5 min", body: "Shape into a boule or batard. Lightly wet the surface and roll the top through the reserved toasted sesame. Place seam-side UP in a well-floured banneton." }, { title: "Proof", time: "See timing tab", body: "Cold retard is especially good here — the long cold ferment mellows the bran's bitterness. Poke test: springs back slowly and partway." }, { title: "Preheat", time: "45 min", body: "Preheat Dutch oven with lid to 500°F for at least 45 min." }, { title: "Score & Bake (Covered)", time: "20 min @ 500°F", body: "Turn onto parchment seam-side DOWN. Score one confident ½-inch arc. Lower into the hot Dutch oven and bake covered at 500°F." }, { title: "Bake (Uncovered)", time: "20–25 min @ 450°F", body: "Remove lid, reduce to 450°F, bake until deep brown and the sesame is toasted. Internal temp 205–210°F. Whole-grain darkens fast — check at 18 min." }, { title: "Cool", time: "1.5+ hr", body: "Cool on a rack at least 1.5 hours. This dense whole-grain loaf needs the extra time — cutting early gives a gummy crumb." }, ], }, honeyoat: { id: "honeyoat", name: "Honey Oat (No-Knead)", emoji: "🍯", tagline: "Soft, honeyed, oat-crusted overnight loaf", color: "#D2A02C", defaultHydration: 78, hydrationRange: [74, 82], defaultLoafSize: "standard", flours: [ { name: "Bread flour", pct: 70, id: "bf" }, { name: "Rolled oats", pct: 21, id: "oats" }, { name: "Whole wheat flour", pct: 9, id: "ww" }, ], starterPct: 12, saltPct: 2, notes: "A soft, honeyed no-knead loaf loaded with rolled oats — mix before bed, bake in the morning. Rolled oats are steamed and pressed flat, so they hydrate and soften on their own during the overnight bulk (no soaker needed, unlike steel cut). The honey feeds early fermentation slightly and gives the signature sweetness and a darker, softer crust. The low 12% starter is calibrated to fit a 10–12 hour overnight window without over-proofing.", tips: [ "Rolled oats go in dry — they soften overnight with no separate soaker", "Low 12% starter is intentional for the overnight schedule; a warm kitchen (72°F+) → drop to 10%", "Honey browns fast — bake cooler than a lean loaf and tent with foil if the top races ahead", "Optional: fold in toasted sunflower or pumpkin seeds; add poppy + sesame to the oat crust", ], extras: { honey: 0.08, oattopping: 0.04, }, steps: [ { title: "Mix Once (Night Before)", time: "5 min", body: "Whisk the flours and rolled oats together. Separately, dissolve the honey into the lukewarm water, then stir in the starter until smooth. Pour over the dry, add salt, and mix with a fork or hand until no dry flour remains — 1–2 min. Rough and sticky is correct. Do NOT knead. Cover tightly." }, { title: "Overnight Bulk", time: "10–12 hrs @ 65–70°F", body: "Leave on the counter overnight. By morning it should be grown 50–75%, bubbly and domed, smelling tangy-sweet, with the oats softened in. Warmer than 72°F? Check at 8–9 hrs." }, { title: "Morning Fold", time: "2 min + 30 min rest", body: "With wet hands, do 4 gentle folds in the bowl (one per side). Cover and rest 30 min. The only 'work' in the recipe." }, { title: "Shape & Oat Crust", time: "2 min", body: "Turn onto a lightly floured surface, fold edges to center into a rough ball, flip seam-down. Wet the top and roll or sprinkle with rolled oats (and poppy/sesame if using) to coat." }, { title: "Preheat", time: "45 min", body: "Preheat Dutch oven with lid to 475°F for at least 45 min while the shaped loaf rests loosely covered." }, { title: "Score & Bake (Covered)", time: "20 min @ 475°F", body: "Transfer to parchment, score one ½-inch slash, lower into the hot Dutch oven. Cover and bake at 475°F. (Cooler than a lean loaf — honey browns fast.)" }, { title: "Bake (Uncovered)", time: "20–25 min @ 425°F", body: "Remove lid, reduce to 425°F, bake until golden and the oats on top are toasted. Internal temp 200–205°F. Tent with foil if darkening too fast." }, { title: "Cool", time: "1+ hr", body: "Cool on a rack at least 1 hour. The honey keeps the crumb moist, so it needs the full cool to set — cutting early gives a gummy texture." }, ], }, dakota: { id: "dakota", name: "Sourdough Dakota", emoji: "🌻", tagline: "Seed medley, bulgur & honey — light and aromatic", color: "#A6702E", defaultHydration: 76, hydrationRange: [72, 82], defaultLoafSize: "standard", flours: [ { name: "Bread flour", pct: 67.5, id: "bf" }, { name: "Whole wheat flour", pct: 30, id: "ww" }, { name: "Vital wheat gluten", pct: 2.5, id: "vwg" }, ], starterPct: 23, saltPct: 2.4, notes: "A sourdough rendition of Café Latte's beloved Dakota Bread (St. Paul, MN). The four-seed medley, bulgur, honey, and a touch of oil give it a light, aromatic character — with wild yeast in place of the original's commercial. The vital wheat gluten keeps it light despite the grains and seeds. Enriched and seed-heavy, so it ferments slower than a lean loaf.", tips: [ "Cracked bulgur (parboiled) can go in unsoaked; ground bulgur acts like flour — add ~15–20ml water and expect less nubbly texture", "Toast the sunflower, pumpkin & sesame for deeper flavor — a nice upgrade over the original's raw seeds", "Bake in a loaf pan for the most authentic sandwich-style loaf, or a Dutch oven for a crustier boule", "Enriched + seedy = ~50–60% rise, not a dramatic double; give it a slightly longer bulk", ], extras: { bulgur: 0.16, // cracked bulgur vs total flour honey: 0.12, oil: 0.08, sunflower: 0.06, pumpkin: 0.08, sesame: 0.02, poppy: 0.016, }, steps: [ { title: "Prep Bulgur & Seeds", time: "5 min", body: "Cracked bulgur can go in unsoaked. Ground bulgur: whisk it in with the flours and add 15–20ml extra water. Optionally toast the sunflower, pumpkin, and sesame in a dry skillet until fragrant, 3–5 min; cool." }, { title: "Mix & Autolyse", time: "45 min", body: "Whisk the flours, vital wheat gluten, and bulgur together. Add water and stir until no dry flour remains. Cover and rest 45 min — the bulgur softens and bran hydrates." }, { title: "Add Starter, Honey, Oil & Salt", time: "5–6 min", body: "Add starter, honey, oil, and salt. Squeeze and fold until the honey and oil are fully worked in. It'll look greasy at first — keep going until smooth and cohesive." }, { title: "Bulk + Add Seeds", time: "6–7 hrs @ 75°F", body: "4 sets of stretch & folds in the first 2 hours. During the SECOND fold, work in all four seeds (reserve a spoonful for the crust), distributing evenly. Ready at 50–60% rise with a domed, bubbly top." }, { title: "Pre-Shape", time: "25 min", body: "Turn onto a lightly floured surface. Fold edges to center, flip seam-down, drag to build tension. Rest uncovered 25 min." }, { title: "Shape & Seed Crust", time: "5 min", body: "LOAF PAN (most authentic): shape into a log, seam-down in a buttered 9x5 pan. DUTCH OVEN: tight boule, seam-up in a floured banneton. Wet the top and press on the reserved seeds." }, { title: "Proof & Preheat", time: "See timing tab", body: "Loaf pan: dough domes ~1 inch above the rim. Boule: poke test springs back slowly and partway. Dutch oven users: preheat it with lid at 450°F for 45 min. Loaf pan: preheat oven to 375°F." }, { title: "Bake", time: "38–45 min (pan)", body: "LOAF PAN: 375°F for 38–45 min to 195–200°F; tent with foil if browning too fast (honey browns quickly). DUTCH OVEN: score, 450°F covered 20 min, then 400°F uncovered 18–22 min to 195–200°F." }, { title: "Cool", time: "1+ hr", body: "Cool on a rack at least 1 hour before slicing. The honey and oil keep the crumb tender, so it needs the full cool to set." }, ], }, }; const LOAF_SIZES = { small: { label: "Small", sub: "~400g loaf", flourG: 350 }, standard: { label: "Standard", sub: "~750g loaf", flourG: 500 }, large: { label: "Large", sub: "~1kg loaf", flourG: 700 }, }; const TIMING = [ { label: "Same-Day", icon: "☀️", color: "#C8903A", schedule: "Start by 8am, bake by 6–8pm", body: "Autolyse 8–9am → Bulk 9am–3pm → Shape 3pm → Room temp proof 3–5 hrs → Bake 6–8pm. Best at 75–78°F. Watch dough signs, not the clock." }, { label: "Overnight", icon: "🌙", color: "#4A6FA5", schedule: "Start afternoon, bake morning", body: "Begin bulk in the afternoon. Shape around 8–9pm. Proof on the counter overnight at 68–70°F for 8–10 hours. Bake straight from the counter in the morning. Works well in a cooler kitchen." }, { label: "Cold Retard", icon: "❄️", color: "#2E7D6B", schedule: "Most flexible & flavorful", body: "Complete bulk same-day. Shape, then refrigerate uncovered 1 hour, then covered 8–16 hours (up to 24 hrs). Bake directly from the fridge — no warming needed. Scores more cleanly and develops deeper sour complexity." }, ]; const TABS = ["Build", "Ingredients", "Method", "Timing", "Knowledge"]; // ─── Knowledge Base Data ────────────────────────────────────────────────────── const VARIABLES = [ { name: "Hydration", icon: "💧", summary: "Water ÷ flour by weight. The master texture lever.", science: "Water mobilizes gluten proteins and gelatinizes starch during baking. More water means more mobile dough, bigger irregular bubbles, and more internal steam to drive oven spring.", more: ["More open, irregular crumb", "Crispier, more blistered crust", "Slacker, stickier, harder to shape", "Faster fermentation"], less: ["Tighter, uniform crumb", "Easier to handle — beginner-friendly", "Denser if pushed too low", "Slower fermentation"], rel: "Whole grains and add-ins like bran and oats demand higher hydration — they absorb more water. A 75% whole-wheat dough handles like a 68% white dough.", }, { name: "Starter %", icon: "🫧", summary: "Weight of starter vs. flour (inoculation). Sets fermentation speed.", science: "Your starter is the starting population of wild yeast (rise) and lactic bacteria (acid/flavor). The amount sets how fast the colony reaches leavening density.", more: ["Faster bulk & proof", "More reliable rise in heavy doughs", "Milder, more lactic tang", "Narrower timing window — easier to over-proof", "Less complex flavor"], less: ["Slower — ideal for overnight schedules", "Deeper, more complex, more sour", "More forgiving timing", "Risk of under-fermenting if starter is weak"], rel: "Inoculation and time are inverse levers to reach the same rise — but produce different flavor. Don't stack high starter AND a long cold retard: you'll over-acidify and get gummy, sharp bread.", }, { name: "Temperature", icon: "🌡️", summary: "Dough & room temp. The most underappreciated variable.", science: "All fermentation is temperature-driven. Rate roughly doubles for every ~15°F rise. Yeast favors 75–82°F; bacteria favor warmth, which is why warm ferments taste tangier.", more: ["(Warmer) Faster everything", "More sour — bacteria outpace yeast", "Easier to over-proof"], less: ["(Cooler) Slower, more controllable", "Milder, or complex in a long cold retard", "Below ~50°F yeast nearly stops — the basis of cold retarding"], rel: "Temperature interacts with everything. A recipe timed for 75°F is wildly off at 65°F (+1–2 hrs bulk) or 82°F (races). Control it with water temp; target a final dough temp of 75–78°F.", }, { name: "Salt", icon: "🧂", summary: "~2% of flour. Small number, big influence. The brake.", science: "Salt shields charges on glutenin so proteins pack tighter (stronger gluten), and it mildly stresses microbes to slow fermentation. It's also essential flavor.", more: ["Tighter, stronger gluten; better gas retention", "Slower fermentation", "More flavor — but >2.5% tastes salty & over-tightens"], less: ["Slacker dough, faster fermentation", "Flat flavor; risk of sticky over-fermented dough"], rel: "Salt is the brake to starter's accelerator. High-hydration and whole-grain doughs often run 2.2% for structure. Don't let concentrated salt sit on the starter — it shocks the microbes.", }, { name: "Flour & Protein", icon: "🌾", summary: "Protein level builds gluten; bran/germ interfere with it.", science: "Protein forms gluten — the elastic gas-trapping network. Bread flour (12–14%) builds strong structure; AP (10–11%) is softer. Bran's sharp particles cut gluten strands; rye has almost no usable gluten.", more: ["(High protein) Stronger structure, better spring, chewier", "Tolerates higher hydration"], less: ["(Whole grain) More flavor & nutrition, faster ferment", "Weaker structure, denser, absorbs more water", "Rye behaves like a paste, not elastic dough"], rel: "This is why the four-grain loaf adds vital wheat gluten — it replaces structural protein bran and oats can't provide. Whole grains also ferment faster (more enzymes & microbe food), so they need shorter bulk.", }, { name: "Inclusions & Soakers", icon: "🥜", summary: "Oats, seeds, bran, nuts — and why to pre-soak them.", science: "Dry inclusions steal water from the dough, leaving a drier crumb and hard bits. A soaker pre-hydrates them; a hot soaker gelatinizes their starch, improving moisture and shelf life. Hard grains like steel cut oats MUST be soaked.", more: ["(Soaked) Even hydration, softer texture, longer shelf life", "Hot soak softens bran's cutting edges"], less: ["(Added dry) Effectively lowers hydration mid-bake", "Hard, pebble-like bits; drier crumb"], rel: "Account for absorption: steel cut oats take ~1.5× their weight in water, bran ~1×. Add heavy inclusions AFTER initial gluten development (a later fold) so they don't impede structure.", }, { name: "Time & Fermentation", icon: "⏳", summary: "Bulk builds flavor & strength; proof sets the final gas.", science: "Yeast make CO₂ (rise); bacteria make lactic & acetic acid (flavor + dough conditioning). Acid slowly degrades gluten — which is why over-fermented dough goes slack. The art is stopping at peak.", more: ["(Longer) More flavor and sourness", "Past peak: slack, sticky, collapses, gummy"], less: ["(Shorter) Milder; if too short: dense, gummy, pale, bursts unpredictably"], rel: "Bulk and proof trade off — long bulk means shorter proof. Cold retarding slows yeast more than bacteria, giving rise control AND extra sour flavor. The most flexible flavor lever you have.", }, { name: "Stretch & Folds", icon: "🤲", summary: "Gentle, time-based gluten development vs. kneading.", science: "Both align protein strands. Kneading uses fast mechanical force; folds work gently over time with rest periods where gluten relaxes and reorganizes. Folds preserve gas bubbles better in wet dough.", more: ["(More folds) Stronger structure, taller loaf, uniform crumb"], less: ["(Fewer / no-knead) Time substitutes for mechanical work", "Slightly less structure, far less effort"], rel: "High-hydration doughs prefer folds over kneading — gentler and better for the crumb. The no-knead method leans entirely on a long ferment to develop gluten passively.", }, ]; const CAUSE_EFFECT = [ ["More open crumb", "↑ hydration, gentle shaping, full bulk", "More water + preserved gas = bigger holes"], ["Tighter crumb", "↓ hydration, degas in shaping", "Less water, fewer/smaller bubbles"], ["More sour", "↑ time, ↓ starter %, cool bulk + cold retard, more whole grain", "Time & cool favor acid-producing bacteria"], ["Less sour / milder", "↓ time, ↑ starter %, warm fast ferment, more white flour", "Less time = less acid; speed favors yeast"], ["Faster schedule", "↑ starter %, ↑ temperature, ↑ whole grain", "All accelerate fermentation"], ["Flexible schedule", "↓ starter %, cold retard, cooler kitchen", "All slow fermentation, widening the window"], ["Better oven spring", "Strong gluten, proper proof, hot oven + steam, confident score", "Structure + steam + a release point"], ["Crispier crust", "↑ hydration, more steam, hotter/longer bake, Dutch oven", "Steam gelatinizes surface, dry heat crisps it"], ["Softer crust", "Add fat, cover while cooling, lower bake temp", "Fat blocks crust formation; trapped steam softens"], ["Chewier crumb", "↑ protein flour, ↑ hydration, longer bake", "More gluten + gelatinized starch = chew"], ["Whole-grain structure", "Add vital wheat gluten, soak bran, ↑ hydration", "Replaces lost gluten; softens bran's edges"], ["Longer shelf life", "Add a soaker, ↑ hydration slightly, touch of fat/honey", "Bound moisture stales slower"], ]; const TROUBLESHOOTING = [ { group: "Crumb & Structure", color: "#8C5A33", items: [ { problem: "Dense, gummy crumb / few holes", cause: "Under-proofed, or weak starter — or cut too soon.", fix: "Ferment longer; use starter at peak (float test); cool 1–2 hrs before cutting. Whole-grain? Add vital wheat gluten and raise hydration." }, { problem: "Flat loaf that spread sideways", cause: "Over-proofed — gluten degraded and can't hold gas.", fix: "Shorten bulk/proof; use the poke test; tighten shaping for more surface tension; drop hydration 3–5% if it's beyond your handling." }, { problem: "Tunneling — holes up top, dense bottom", cause: "Under-proofed and/or uneven shaping.", fix: "Proof a bit longer; shape more evenly for uniform gas distribution." }, { problem: "No rise at all", cause: "Starter inactive, or microbes killed by hot water.", fix: "Feed starter, wait for reliable doubling + float test before using. Keep all added liquids lukewarm (<100°F)." }, ], }, { group: "Crust & Bake", color: "#C8903A", items: [ { problem: "Crust too pale", cause: "Oven not hot enough / under-preheated.", fix: "Preheat Dutch oven 45+ min at 500°F; bake longer uncovered; a little honey or milk aids browning." }, { problem: "Crust too thick / hard", cause: "Baked too long uncovered or too dry.", fix: "Reduce uncovered time; ensure enough steam during the covered phase." }, { problem: "No oven spring / no ear", cause: "Over-proofed, timid scoring, or cool oven.", fix: "Proof slightly less; score one confident ½-inch slash at 30–45°; preheat longer." }, { problem: "Bursting / blowing out the side", cause: "Under-proofed or poor scoring.", fix: "Proof longer; score decisively to give expansion a deliberate path." }, ], }, { group: "Dough Handling", color: "#2E7D6B", items: [ { problem: "Dough is soup / won't come together", cause: "Hydration too high for the flour, or over-fermented.", fix: "Lower hydration; add folds; use higher-protein flour; check you didn't blow past peak bulk." }, { problem: "Stiff / tearing / won't stretch", cause: "Hydration too low or under-rested gluten.", fix: "Add water (bassinage) or raise hydration next batch; rest between folds so gluten relaxes." }, { problem: "Sticky even at moderate hydration", cause: "Under-developed gluten or over-fermented.", fix: "More/earlier folds; ferment less; wet hands instead of flouring to handle." }, ], }, { group: "Flavor", color: "#7A4F3A", items: [ { problem: "Not sour enough", cause: "Ferment too fast/warm or starter too mild.", fix: "Longer, cooler ferment; lower starter %; add a long cold retard; include whole grain or a bit of rye." }, { problem: "Too sour / sharp", cause: "Over-fermented or starter past peak.", fix: "Shorter ferment; higher starter %; warmer fast ferment; feed starter more often and use at peak; cut cold-retard time." }, { problem: "Bland / flat", cause: "Too little salt or too little fermentation.", fix: "Check salt is ~2%; ferment longer for flavor development; add some whole grain." }, ], }, ]; // ─── Hydration Slider ───────────────────────────────────────────────────────── function HydrationSlider({ value, min, max, defaultVal, onChange, color }) { const pct = ((value - min) / (max - min)) * 100; const defaultPct = ((defaultVal - min) / (max - min)) * 100; const isDefault = value === defaultVal; const label = value < 71 ? "Low — easy to handle, tighter crumb" : value < 76 ? "Balanced — classic open crumb" : value < 81 ? "High — open crumb, wetter dough" : "Very high — experienced bakers only"; return (
Hydration
{!isDefault && ( )} {value}%
{/* Track area — tall div for easy touch targeting */}
{/* Background track */}
{/* Filled portion */}
{/* Default value tick mark */}
{/* Thumb (visual only, pointer-events: none) */}
{/* Actual range input — transparent, covers full touch area */} onChange(Number(e.target.value))} style={{ position: "absolute", inset: 0, width: "100%", height: "100%", opacity: 0, cursor: "pointer", margin: 0, padding: 0, WebkitAppearance: "none", appearance: "none", }} />
{min}% {label} {max}%
); } // ─── Main App ───────────────────────────────────────────────────────────────── export default function SourdoughBuilder() { const [selectedBread, setSelectedBread] = useState("white"); const [hydration, setHydration] = useState(BREADS.white.defaultHydration); const [loaves, setLoaves] = useState(1); const [loafSize, setLoafSize] = useState("standard"); const [starterPct, setStarterPct] = useState(BREADS.white.starterPct); const [tab, setTab] = useState("Build"); const [checkedSteps, setCheckedSteps] = useState({}); const [checkedIngredients, setCheckedIngredients] = useState({}); const [expandedVar, setExpandedVar] = useState(null); const [knowledgeView, setKnowledgeView] = useState("variables"); const bread = BREADS[selectedBread]; const accentColor = bread.color; const isModified = hydration !== bread.defaultHydration || loafSize !== "standard" || loaves !== 1 || starterPct !== bread.starterPct; const resetToDefaults = useCallback(() => { setHydration(bread.defaultHydration); setLoafSize("standard"); setLoaves(1); setStarterPct(bread.starterPct); }, [bread.defaultHydration, bread.starterPct]); useEffect(() => { setHydration(bread.defaultHydration); setLoafSize("standard"); setLoaves(1); setStarterPct(bread.starterPct); setCheckedSteps({}); setCheckedIngredients({}); }, [selectedBread]); // ── Calculations ───────────────────────────────────────────────────────── // Starter % drives total flour when adjusted — hydration stays fixed. // totalFlour is the "true" flour (excluding starter contribution). const flourPerLoaf = LOAF_SIZES[loafSize].flourG; const totalFlour = flourPerLoaf * loaves; const starterG = Math.round(totalFlour * (starterPct / 100)); // Starter itself is ~50% flour + 50% water (100% hydration starter). // Adjust main water to keep overall dough hydration fixed. const starterFlourContrib = starterG * 0.5; const starterWaterContrib = starterG * 0.5; const mainWaterG = Math.round(totalFlour * (hydration / 100) - starterWaterContrib); const waterG = Math.max(0, mainWaterG); const saltG = parseFloat((totalFlour * (bread.saltPct / 100)).toFixed(1)); // Effective hydration for display (accounts for starter water) const effectiveHydration = Math.round(((waterG + starterWaterContrib) / totalFlour) * 100); // Four-grain single-soak: oats + bran absorb water, so the soaker volume is boosted // above the free-water target to compensate (oats ~1.5x weight, bran ~1x weight). const fg = bread.extras || {}; const fgOatsG = selectedBread === "fourgrain" ? Math.round(totalFlour * (fg.oats || 0)) : 0; const fgBranPct = (bread.flours.find(f => f.id === "bran") || {}).pct || 0; const fgBranG = selectedBread === "fourgrain" ? Math.round(totalFlour * (fgBranPct / 100)) : 0; const fgAbsorbedG = Math.round(fgOatsG * 1.5 + fgBranG * 1.0); const fgSoakerWaterG = waterG + fgAbsorbedG; const flourBreakdown = bread.flours.map(f => ({ ...f, grams: Math.round(totalFlour * (f.pct / 100)), })); const ingredients = [ ...flourBreakdown.map(f => ({ id: f.id, name: f.name, amount: f.grams, unit: "g" })), selectedBread === "fourgrain" ? { id: "water", name: "Boiling water (soak oats + bran in this)", amount: fgSoakerWaterG, unit: "ml", note: `≈${waterG}ml free after absorption` } : { id: "water", name: "Water (lukewarm ~80°F)", amount: waterG, unit: "ml" }, { id: "starter", name: `Active starter (${starterPct}%)`, amount: starterG, unit: "g", adjustable: true }, { id: "salt", name: "Fine sea salt", amount: saltG, unit: "g" }, ]; if (selectedBread === "enriched") { const milkG = Math.round(totalFlour * 0.55); const butterG = Math.round(totalFlour * 0.08); const honeyG = Math.round(totalFlour * 0.03); ingredients.splice(flourBreakdown.length, 0, { id: "milk", name: "Whole milk (room temp, replaces water)", amount: milkG, unit: "ml" } ); ingredients.push({ id: "butter", name: "Unsalted butter (softened)", amount: butterG, unit: "g" }); ingredients.push({ id: "honey", name: "Honey", amount: honeyG, unit: "g" }); } if (selectedBread === "seeded") { const seedsG = Math.round(totalFlour * 0.15); ingredients.push({ id: "seeds", name: "Mixed seeds (sunflower, sesame, flax)", amount: seedsG, unit: "g" }); ingredients.push({ id: "seedwater", name: "Boiling water for soaker", amount: seedsG, unit: "ml" }); } if (selectedBread === "fourgrain" && bread.extras) { const e = bread.extras; // Insert steel cut oats right after the flours (it's part of the soaker) ingredients.splice(flourBreakdown.length, 0, { id: "oats", name: "Steel cut oats (soak with bran)", amount: fgOatsG, unit: "g" } ); ingredients.push({ id: "honey", name: "Honey", amount: Math.round(totalFlour * e.honey), unit: "g" }); ingredients.push({ id: "sesame", name: "Sesame seeds (toasted)", amount: Math.round(totalFlour * e.sesame), unit: "g" }); } if (selectedBread === "honeyoat" && bread.extras) { const e = bread.extras; ingredients.push({ id: "honey", name: "Honey (dissolve into the water)", amount: Math.round(totalFlour * e.honey), unit: "g" }); ingredients.push({ id: "oattopping", name: "Rolled oats, for topping", amount: Math.round(totalFlour * e.oattopping), unit: "g" }); } if (selectedBread === "dakota" && bread.extras) { const e = bread.extras; // bulgur sits with the flours; seeds and enrichments after ingredients.splice(flourBreakdown.length, 0, { id: "bulgur", name: "Bulgur cracked wheat", amount: Math.round(totalFlour * e.bulgur), unit: "g" } ); ingredients.push({ id: "honey", name: "Honey", amount: Math.round(totalFlour * e.honey), unit: "g" }); ingredients.push({ id: "oil", name: "Canola oil", amount: Math.round(totalFlour * e.oil), unit: "ml" }); ingredients.push({ id: "sunflower", name: "Sunflower seeds", amount: Math.round(totalFlour * e.sunflower), unit: "g" }); ingredients.push({ id: "pumpkin", name: "Pumpkin seeds, chopped", amount: Math.round(totalFlour * e.pumpkin), unit: "g" }); ingredients.push({ id: "sesame", name: "Sesame seeds", amount: Math.round(totalFlour * e.sesame), unit: "g" }); ingredients.push({ id: "poppy", name: "Poppy seeds", amount: Math.round(totalFlour * e.poppy), unit: "g" }); } const toggleStep = id => setCheckedSteps(p => ({ ...p, [id]: !p[id] })); const toggleIngredient = id => setCheckedIngredients(p => ({ ...p, [id]: !p[id] })); return (
{/* ── Header ── */}
Sourdough Builder

{bread.emoji} {bread.name}

{bread.tagline}

{[["Hydration", `${effectiveHydration}%`], ["Flour", `${totalFlour}g`], ["Water", `${waterG}ml`], ["Starter", `${starterG}g`], ["Salt", `${saltG}g`]].map(([k, v]) => (
{k} {v}
))}
{/* ── Tabs ── */}
{TABS.map(t => ( ))}
{/* ── Content ── */}
{/* ══ BUILD ══ */} {tab === "Build" && (
{/* Bread selector */}
Choose Your Bread
{Object.values(BREADS).map(b => ( ))}
{/* Controls card */}
Adjust Your Dough
{isModified && ( )}
{/* Hydration slider */}
{/* Loaf size */}
Loaf Size
{Object.entries(LOAF_SIZES).map(([key, sz]) => ( ))}
{/* Loaves counter */}
Number of Loaves
{loaves}
{/* Flour breakdown */}
Flour Breakdown
{flourBreakdown.map((f, i) => (
{f.name} {f.grams}g ({f.pct}%)
))}
{/* Notes & tips */}

{bread.notes}

Baker's Tips
{bread.tips.map((tip, i) => (
{tip}
))}
)} {/* ══ INGREDIENTS ══ */} {tab === "Ingredients" && (

{loaves} × {LOAF_SIZES[loafSize].label.toLowerCase()} loaf{loaves > 1 ? "ves" : ""} at {hydration}% hydration. Adjust starter % below — water recalculates automatically to keep hydration fixed.

{selectedBread === "fourgrain" && (
Single-soak method: soak the steel cut oats + wheat bran together in the full {fgSoakerWaterG}ml of boiling water for 2+ hrs, cool to lukewarm, then dump the entire mass into the dough. The {fgAbsorbedG}ml the oats and bran absorb is already built into that number — about {waterG}ml ends up as free water.
)} {ingredients.map(ing => (
toggleIngredient(ing.id)} >
{ ev.stopPropagation(); toggleIngredient(ing.id); } : undefined} style={{ width: "20px", height: "20px", borderRadius: "4px", border: `2px solid ${accentColor}`, background: checkedIngredients[ing.id] ? accentColor : "transparent", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, cursor: "pointer", }}> {checkedIngredients[ing.id] && }
{ing.name} {ing.adjustable && (
Adjust % → water updates to preserve {hydration}% hydration
)} {ing.note && (
{ing.note}
)}
{ing.adjustable ? ( /* Starter adjuster */
{starterG}g {starterPct}% inoculation {starterPct !== bread.starterPct && ( setStarterPct(bread.starterPct)} style={{ color: accentColor, cursor: "pointer", marginLeft: "4px" }}>↺ )}
) : ( {ing.amount}{ing.unit} )}
))} {/* Baker's Math */}
Baker's Math
{[ ["Hydration (target)", `${hydration}%`], ["Total Flour", `${totalFlour}g`], ["Main Water", `${waterG}ml`], ["Starter", `${starterG}g (${starterPct}%)`], ["Salt", `${saltG}g (${bread.saltPct}%)`], ["Total Dough", `${totalFlour + waterG + starterG + Math.round(saltG)}g`], ].map(([l, v]) => (
{l}
{v}
))}
{starterPct !== bread.starterPct && (
{starterPct > bread.starterPct ? `↑ Higher inoculation (${starterPct}% vs default ${bread.starterPct}%) — expect faster fermentation. Reduce bulk time accordingly.` : `↓ Lower inoculation (${starterPct}% vs default ${bread.starterPct}%) — slower, more complex fermentation. Good for overnight or cold retard.`}
)}
)} {/* ══ METHOD ══ */} {tab === "Method" && (

Steps for {bread.name}. Tap to check off as you go.

{bread.steps.map((step, i) => (
toggleStep(step.title)} style={{ display: "flex", gap: "12px", marginBottom: "10px", cursor: "pointer" }}>
{checkedSteps[step.title] ? "✓" : i + 1}
{step.title} {step.time}

{step.body}

))}
)} {/* ══ TIMING ══ */} {tab === "Timing" && (

{(selectedBread === "noknead" || selectedBread === "honeyoat") ? "This recipe is built for the Overnight schedule. Same-day and cold retard are possible but require adjusting starter % — see notes below." : "Three schedules to fit your life. Cold retard is the most flexible and develops the best flavor."}

{selectedBread === "noknead" && (
🌙 Overnight is the ideal schedule for this recipe

Mix at 9–10pm → Bulk overnight 10–12 hrs → Morning fold → Rest 30 min → Bake 8–9am. The 10% starter and cool counter temp are calibrated for this window. If your kitchen runs above 72°F, drop starter to 8% or mix with slightly cool water.

)} {selectedBread === "honeyoat" && (
🍯 Built for an overnight bake

Mix at 9–10pm → Bulk overnight 10–12 hrs → Morning fold → Rest 30 min → Shape → Bake by 9–10am. The 12% starter suits this window. Warm kitchen (72°F+)? Drop to 10% or check the dough at 8–9 hrs. For a same-day loaf, bump starter to 20% and bulk 5–6 hrs at 75°F.

)} {TIMING.map(t => (
{t.icon} {t.label}
{t.schedule}

{t.body}

))}
🌡️ Temperature is everything

A cold kitchen (65°F) adds 1–2 hours to bulk fermentation. A warm one (78°F+) cuts it significantly. Always trust dough signs — rise, jiggle, surface bubbles — over the clock.

)} {/* ══ KNOWLEDGE ══ */} {tab === "Knowledge" && (
{/* Sub-nav */}
{[["variables", "Variables"], ["cause", "Cause & Effect"], ["fixes", "Troubleshooting"]].map(([key, lbl]) => ( ))}
{/* ── Variables (expandable) ── */} {knowledgeView === "variables" && (

Sourdough is a system of interacting levers — pull one and the others move. Tap any variable to expand the science and relationships.

{VARIABLES.map((v, i) => { const open = expandedVar === i; return (
{open && (
The Science {v.science}
▲ MORE
{v.more.map((m, j) => (
·{m}
))}
▼ LESS
{v.less.map((m, j) => (
·{m}
))}
How it connects

{v.rel}

)}
); })}
)} {/* ── Cause & Effect table ── */} {knowledgeView === "cause" && (

Quick reference: what to change to get the result you want, and why it works.

{CAUSE_EFFECT.map(([want, change, why], i) => (
{want}
{change}
{why}
))}
)} {/* ── Troubleshooting ── */} {knowledgeView === "fixes" && (

Almost every problem traces to starter health or fermentation degree. Find your symptom below.

{TROUBLESHOOTING.map(group => (
{group.group}
{group.items.map((it, j) => (
⚠ {it.problem}
Likely cause: {it.cause}
Fix: {it.fix}
))}
))} {/* Master diagnostics */}
The Two Master Diagnostics
1. Starter health

Is it vigorous, doubling reliably, passing the float test at peak? A weak starter causes dense loaves and poor flavor no matter what else you do. Fix the starter first.

2. Fermentation degree (the poke test)

Press a floured finger ½ inch into proofed dough. Springs back fast & fully = under-proofed, give it more time. Slowly & halfway = ready. Doesn't spring back = over-proofed; bake now, shorten next time.

The golden rule: read the dough, not the clock.
)}
)}
); }