{"id":1206,"date":"2025-10-21T18:59:16","date_gmt":"2025-10-22T01:59:16","guid":{"rendered":"https:\/\/brianbaker.net\/blog\/?p=1206"},"modified":"2025-10-21T21:22:06","modified_gmt":"2025-10-22T04:22:06","slug":"homemade-ramen","status":"publish","type":"post","link":"https:\/\/brianbaker.net\/blog\/2025\/10\/21\/homemade-ramen\/","title":{"rendered":"Homemade Ramen"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"default\">\n<head>\n  <meta charset=\"UTF-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \/>\n  <title>Homemade Ramen \u2014 Ultimate Interactive Recipe<\/title>\n  <meta name=\"description\" content=\"Foolproof weeknight shoyu ramen with interactive timers, serving adjuster, nutrition chart, and shopping list tools.\" \/>\n  <meta property=\"og:title\" content=\"Homemade Ramen \u2014 Ultimate Interactive Recipe\" \/>\n  <meta property=\"og:description\" content=\"Foolproof weeknight shoyu ramen with interactive timers, serving adjuster, nutrition chart, and shopping list tools.\" \/>\n  <meta property=\"og:type\" content=\"article\" \/>\n  <meta property=\"og:image\" content=\"\" \/>\n  <meta property=\"og:url\" content=\"\" \/>\n  <meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\n  <link rel=\"preconnect\" href=\"https:\/\/cdnjs.cloudflare.com\" crossorigin>\n  <link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n  <link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n  <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;600;700&#038;family=Playfair+Display:wght@700&#038;display=swap\" rel=\"stylesheet\">\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/Chart.js\/4.4.1\/chart.umd.min.js\" integrity=\"sha512-7qA7m+lsPqL9S2HfbPBGQfblafm3hXiv2dQWy6rO5q3zF1RFSsM9h8H2Q+pmVPC8yf4V1unNn9KfJ3h8d4O8xQ==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"><\/script>\n\n  <style>\n    :root {\n      --bg: #fff;\n      --surface: #f7f7fa;\n      --text: #1a1a1a;\n      --muted: #6b7280;\n      --primary: #e11d48; \/* rose-600 *\/\n      --primary-contrast: #fff;\n      --accent: #10b981; \/* emerald-500 *\/\n      --warning: #f59e0b; \/* amber-500 *\/\n      --success: #16a34a; \/* green-600 *\/\n      --danger: #ef4444; \/* red-500 *\/\n      --card: #ffffff;\n      --shadow: 0 10px 30px rgba(0,0,0,0.08);\n      --radius: 16px;\n      --step: #2563eb; \/* blue-600 *\/\n      --link: #2563eb;\n    }\n    [data-theme=\"high-contrast\"] {\n      --bg: #000;\n      --surface: #111;\n      --text: #fff;\n      --muted: #d1d5db;\n      --primary: #ff006e;\n      --primary-contrast: #000;\n      --accent: #00ff7f;\n      --warning: #ffd000;\n      --success: #00ff00;\n      --danger: #ff4040;\n      --card: #000;\n      --shadow: 0 0 0 3px #fff;\n      --step: #00b3ff;\n      --link: #00b3ff;\n    }\n\n    * { box-sizing: border-box; }\n    html, body { margin: 0; padding: 0; }\n    body {\n      font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", \"Apple Color Emoji\", \"Segoe UI Emoji\";\n      background: var(--bg);\n      color: var(--text);\n      line-height: 1.6;\n    }\n    a { color: var(--link); text-decoration: none; }\n    a:hover { text-decoration: underline; }\n\n    header.hero {\n      display: grid;\n      grid-template-columns: 1.2fr 1fr;\n      gap: 24px;\n      padding: 32px 24px 16px;\n      align-items: center;\n    }\n    .hero-title {\n      font-family: \"Playfair Display\", serif;\n      font-size: clamp(2rem, 4vw, 3rem);\n      margin: 0 0 8px;\n    }\n    .sub {\n      color: var(--muted);\n      margin-bottom: 16px;\n    }\n    .badges { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 0; }\n    .badge {\n      display: inline-flex; align-items: center; gap: 6px;\n      background: var(--surface); color: var(--text);\n      padding: 8px 12px; border-radius: 999px; font-size: 0.9rem;\n    }\n    .badge.difficulty-easy { border: 2px solid #22c55e; }\n    .badge.difficulty-medium { border: 2px solid #f59e0b; }\n    .badge.difficulty-hard { border: 2px solid #ef4444; }\n\n    .hero-img {\n      position: relative;\n      border-radius: var(--radius);\n      overflow: hidden;\n      box-shadow: var(--shadow);\n      min-height: 260px;\n      background: linear-gradient(120deg, #fff1f2, #f0f9ff);\n    }\n    .hero-img img { width: 100%; height: 100%; object-fit: cover; display: block; }\n    .hc-toggle {\n      display: inline-flex; gap: 8px; align-items: center;\n      background: var(--surface); padding: 8px 12px; border-radius: 999px; cursor: pointer;\n      border: 1px solid rgba(0,0,0,0.08);\n    }\n\n    main { padding: 0 24px 120px; }\n\n    \/* Layout *\/\n    .content {\n      display: grid; grid-template-columns: 1fr 320px; gap: 24px; align-items: start;\n    }\n\n    .card {\n      background: var(--card); border-radius: var(--radius); box-shadow: var(--shadow);\n      padding: 20px; position: relative;\n    }\n\n    .sticky-card {\n      position: sticky; top: 16px; z-index: 10;\n    }\n\n    .quick-card ul { list-style: none; padding-left: 0; margin: 0; }\n    .quick-card li { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px dashed rgba(0,0,0,0.08); }\n\n    .diet-icons span { font-size: 1.2rem; margin-right: 8px; }\n\n    .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }\n    .grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }\n\n    .ingredients header, .instructions header { display: flex; align-items: center; justify-content: space-between; }\n\n    .controls { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }\n    .btn {\n      display: inline-flex; align-items: center; justify-content: center; gap: 8px;\n      background: var(--primary); color: var(--primary-contrast);\n      padding: 10px 14px; border-radius: 12px; border: none; cursor: pointer; font-weight: 600;\n      box-shadow: var(--shadow);\n    }\n    .btn.secondary { background: var(--surface); color: var(--text); }\n    .btn.ghost { background: transparent; color: var(--text); border: 1px solid rgba(0,0,0,0.12); }\n    .btn:disabled { opacity: 0.6; cursor: not-allowed; }\n\n    .checkbox { width: 22px; height: 22px; border-radius: 6px; border: 2px solid #94a3b8; display: inline-grid; place-items: center; cursor: pointer; background: #fff; }\n    .checkbox.checked { background: var(--accent); border-color: var(--accent); color: #fff; }\n\n    .ingredient { display: grid; grid-template-columns: 28px 1fr auto; gap: 10px; align-items: center; padding: 8px 0; border-bottom: 1px solid rgba(0,0,0,0.06); }\n    .ingredient .info { color: var(--muted); font-size: 0.9rem; }\n    .ingredient .sub-icon { cursor: pointer; padding: 0 8px; color: var(--link); }\n\n    .converter, .notes, .faq, .ratings, .shopping-list, .variations, .timers, .nutrition, .tools {\n      margin-top: 16px;\n    }\n\n    .step { position: relative; padding: 16px 16px 16px 56px; background: var(--surface); border-radius: 12px; margin-bottom: 12px; }\n    .step-number { position: absolute; left: 16px; top: 16px; width: 28px; height: 28px; background: var(--step); color: #fff; border-radius: 50%; display: grid; place-items: center; font-weight: 700; }\n    .step img { width: 100%; border-radius: 10px; margin-top: 12px; }\n    .step .tips { margin-top: 8px; }\n    .collapsible { background: transparent; border: none; color: var(--link); cursor: pointer; }\n    .collapse-content { display: none; background: #fff; border-radius: 10px; padding: 12px; border: 1px solid rgba(0,0,0,0.06); }\n    .collapse-content.open { display: block; }\n\n    .progress { height: 10px; background: #e5e7eb; border-radius: 999px; overflow: hidden; }\n    .progress > span { display: block; height: 100%; width: 0%; background: linear-gradient(90deg, var(--accent), #34d399); transition: width 0.3s; }\n\n    .video-embed { position: relative; padding-top: 56.25%; border-radius: 12px; overflow: hidden; background: #000; }\n    .video-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }\n\n    .timer { display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: center; background: var(--surface); border-radius: 12px; padding: 12px; margin-bottom: 8px; }\n    .timer-controls { display: flex; gap: 6px; }\n\n    .print-note { font-size: 0.9rem; color: var(--muted); }\n\n    .carousel { display: grid; grid-auto-flow: column; grid-auto-columns: 70%; gap: 16px; overflow-x: auto; scroll-snap-type: x mandatory; padding-bottom: 8px; }\n    .carousel .card { scroll-snap-align: start; }\n\n    .sr-only { position: absolute; left: -10000px; top: auto; width: 1px; height: 1px; overflow: hidden; }\n\n    \/* Responsive *\/\n    @media (max-width: 1000px) {\n      .content { grid-template-columns: 1fr; }\n      .sticky-card { position: static; }\n      header.hero { grid-template-columns: 1fr; }\n    }\n\n    \/* Print styles *\/\n    @media print {\n      header, .sticky-card, .tools, .ratings, .faq, .shopping-list, .timers, .converter, .video, .share { display: none !important; }\n      body { color: #000; }\n      .card { box-shadow: none; border: 1px solid #000; }\n    }\n  <\/style>\n<\/head>\n<body>\n  <a class=\"sr-only\" href=\"#main\">Skip to content<\/a>\n  <header class=\"hero\" role=\"banner\">\n    <div>\n      <h1 class=\"hero-title\">Homemade Ramen (Weeknight Shoyu)<\/h1>\n      <p class=\"sub\">Deep, savory broth in under 90 minutes with silky noodles and classic toppings. Interactive tools make it foolproof.<\/p>\n      <div class=\"badges\" aria-label=\"recipe badges\">\n        <span class=\"badge\" title=\"Prep time\">\u23f1\ufe0f Prep: <strong>25 min<\/strong><\/span>\n        <span class=\"badge\" title=\"Cook time\">\ud83c\udf73 Cook: <strong>60 min<\/strong><\/span>\n        <span class=\"badge\" title=\"Servings\">\ud83c\udf5c Serves: <strong id=\"servings-label\">4<\/strong><\/span>\n        <span class=\"badge difficulty-medium\" title=\"Difficulty\">\ud83c\udff7\ufe0f Difficulty: <strong>Medium<\/strong><\/span>\n      <\/div>\n      <div class=\"badges\" style=\"margin-top:12px;\">\n        <button id=\"contrastToggle\" class=\"hc-toggle\" aria-pressed=\"false\" aria-label=\"Toggle high contrast mode\">\ud83c\udf13 High-contrast<\/button>\n        <button id=\"printBtn\" class=\"hc-toggle\" aria-label=\"Print this recipe\">\ud83d\udda8\ufe0f Print<\/button>\n        <span class=\"print-note\">Tip: Use high-contrast mode while cooking.<\/span>\n      <\/div>\n    <\/div>\n    <figure class=\"hero-img\" aria-label=\"Recipe hero image\">\n      <img decoding=\"async\" id=\"heroImage\" src=\"https:\/\/images.unsplash.com\/photo-1542826438-bd32f43d626f?q=80&#038;w=1600&#038;auto=format&#038;fit=crop\" alt=\"Bowl of shoyu ramen with noodles, egg, scallions, and nori\" \/>\n    <\/figure>\n  <\/header>\n\n  <main id=\"main\" class=\"content\" role=\"main\">\n    <article class=\"card\" aria-labelledby=\"overview\">\n      <h2 id=\"overview\">Recipe Overview<\/h2>\n      <div class=\"grid-2\">\n        <section class=\"ingredients\" aria-labelledby=\"ingredients-title\">\n          <header>\n            <h3 id=\"ingredients-title\">Ingredients<\/h3>\n            <div class=\"controls\" role=\"group\" aria-label=\"ingredient controls\">\n              <label for=\"unitToggle\">Units:<\/label>\n              <select id=\"unitToggle\" aria-label=\"Unit system\">\n                <option value=\"metric\">Metric<\/option>\n                <option value=\"imperial\">US<\/option>\n              <\/select>\n\n              <label for=\"servingInput\">Servings:<\/label>\n              <input id=\"servingInput\" type=\"number\" min=\"1\" value=\"4\" aria-label=\"Servings\" style=\"width:80px;\" \/>\n              <button id=\"servingMinus\" class=\"btn secondary\" aria-label=\"Decrease servings\">\u2212<\/button>\n              <button id=\"servingPlus\" class=\"btn secondary\" aria-label=\"Increase servings\">+<\/button>\n\n              <button id=\"addSelectedToList\" class=\"btn\" aria-label=\"Add selected ingredients to shopping list\">Add to Shopping List<\/button>\n            <\/div>\n          <\/header>\n\n          <div id=\"ingredientsList\" aria-live=\"polite\">\n            <!-- Ingredients populated by JS -->\n          <\/div>\n\n          <div class=\"tools\">\n            <h4>Kitchen Measurement Converter<\/h4>\n            <div class=\"grid-3\">\n              <div class=\"card\">\n                <label>Volume \u279c Metric<\/label>\n                <div class=\"grid-3\" style=\"grid-template-columns: 1fr auto 1fr;\">\n                  <input id=\"convCups\" type=\"number\" min=\"0\" step=\"0.01\" placeholder=\"cups\" aria-label=\"cups\" \/>\n                  <span aria-hidden=\"true\" style=\"align-self:center;\">=<\/span>\n                  <input id=\"convMl\" type=\"text\" placeholder=\"ml\" aria-label=\"milliliters\" readonly \/>\n                <\/div>\n                <button class=\"btn secondary\" onclick=\"document.getElementById('convMl').value = (Number(document.getElementById('convCups').value||0)*240).toFixed(0) + ' ml'\">Convert<\/button>\n              <\/div>\n              <div class=\"card\">\n                <label>Weight \u279c Imperial<\/label>\n                <div class=\"grid-3\" style=\"grid-template-columns: 1fr auto 1fr;\">\n                  <input id=\"convGrams\" type=\"number\" min=\"0\" step=\"1\" placeholder=\"grams\" aria-label=\"grams\" \/>\n                  <span aria-hidden=\"true\" style=\"align-self:center;\">=<\/span>\n                  <input id=\"convOz\" type=\"text\" placeholder=\"oz\" aria-label=\"ounces\" readonly \/>\n                <\/div>\n                <button class=\"btn secondary\" onclick=\"document.getElementById('convOz').value = (Number(document.getElementById('convGrams').value||0)\/28.3495).toFixed(2) + ' oz'\">Convert<\/button>\n              <\/div>\n              <div class=\"card\">\n                <label>Ingredient Density (approx.)<\/label>\n                <select id=\"densityIngredient\" aria-label=\"ingredient\">\n                  <option value=\"water\">Water\/Broth (1 ml = 1 g)<\/option>\n                  <option value=\"miso\">Miso Paste (~1 ml = 1.1 g)<\/option>\n                  <option value=\"soy\">Soy Sauce (~1 ml = 1.2 g)<\/option>\n                  <option value=\"oil\">Oil (~1 ml = 0.91 g)<\/option>\n                <\/select>\n                <div class=\"grid-3\" style=\"grid-template-columns: 1fr auto 1fr;\">\n                  <input id=\"densityMl\" type=\"number\" placeholder=\"ml\" \/>\n                  <span aria-hidden=\"true\" style=\"align-self:center;\">\u2248<\/span>\n                  <input id=\"densityG\" type=\"text\" placeholder=\"grams\" readonly \/>\n                <\/div>\n                <button class=\"btn secondary\" onclick=\"convertDensity()\">Estimate<\/button>\n              <\/div>\n            <\/div>\n          <\/div>\n        <\/section>\n\n        <section class=\"instructions\" aria-labelledby=\"instructions-title\">\n          <header>\n            <h3 id=\"instructions-title\">Step-by-Step Instructions<\/h3>\n            <div class=\"controls\">\n              <div class=\"progress\" style=\"width:220px;\" aria-label=\"progress\" aria-live=\"polite\"><span id=\"progressBar\"><\/span><\/div>\n              <span id=\"progressLabel\" class=\"sub\" aria-live=\"polite\">0% complete<\/span>\n            <\/div>\n          <\/header>\n          <div id=\"steps\">\n            <!-- Steps populated by JS -->\n          <\/div>\n        <\/section>\n      <\/div>\n\n      <section class=\"video\" aria-labelledby=\"video-title\" style=\"margin-top:16px;\">\n        <h3 id=\"video-title\">Video Tutorial<\/h3>\n        <div class=\"grid-2\">\n          <div>\n            <div class=\"video-embed\" aria-label=\"Embedded video\">\n              <iframe id=\"videoFrame\" src=\"\" title=\"Ramen Video\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen><\/iframe>\n            <\/div>\n            <div class=\"controls\" style=\"margin-top:8px;\">\n              <input id=\"videoUrl\" type=\"url\" placeholder=\"Paste YouTube URL (optional)\" style=\"flex:1;\" aria-label=\"Video URL\" \/>\n              <button class=\"btn secondary\" onclick=\"setVideo()\">Embed<\/button>\n            <\/div>\n          <\/div>\n          <aside class=\"card\" aria-label=\"Key step timestamps\">\n            <h4>Timestamps<\/h4>\n            <ul>\n              <li><button class=\"btn ghost\" onclick=\"seekTo(0)\">0:00 Intro<\/button><\/li>\n              <li><button class=\"btn ghost\" onclick=\"seekTo(60*5)\">5:00 Broth aromatics<\/button><\/li>\n              <li><button class=\"btn ghost\" onclick=\"seekTo(60*20)\">20:00 Tare sauce<\/button><\/li>\n              <li><button class=\"btn ghost\" onclick=\"seekTo(60*45)\">45:00 Eggs &#038; noodles<\/button><\/li>\n              <li><button class=\"btn ghost\" onclick=\"seekTo(60*70)\">70:00 Assemble bowls<\/button><\/li>\n            <\/ul>\n          <\/aside>\n        <\/div>\n      <\/section>\n\n      <section class=\"nutrition card\" aria-labelledby=\"nutrition-title\">\n        <h3 id=\"nutrition-title\">Nutrition (per serving)<\/h3>\n        <div class=\"grid-3\">\n          <div>\n            <ul class=\"quick-card\">\n              <li><span>Calories<\/span><strong id=\"calories\">650 kcal<\/strong><\/li>\n              <li><span>Protein<\/span><strong id=\"protein\">35 g<\/strong><\/li>\n              <li><span>Carbs<\/span><strong id=\"carbs\">70 g<\/strong><\/li>\n              <li><span>Fat<\/span><strong id=\"fat\">22 g<\/strong><\/li>\n              <li><span>Sodium<\/span><strong>~2,400 mg<\/strong><\/li>\n              <li><span>Fiber<\/span><strong>5 g<\/strong><\/li>\n              <li><span>Vitamins<\/span><strong>B, K, Iron<\/strong><\/li>\n            <\/ul>\n          <\/div>\n          <div class=\"card\"><canvas id=\"macroChart\" aria-label=\"Macro distribution chart\" role=\"img\"><\/canvas><\/div>\n          <div class=\"card\">\n            <h4>Notes<\/h4>\n            <p>Values are estimates for shoyu ramen with eggs and chicken. Adjust with serving size and variations below.<\/p>\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <section class=\"variations card\" aria-labelledby=\"variations-title\">\n        <h3 id=\"variations-title\">Recipe Variations<\/h3>\n        <div class=\"grid-3\" role=\"tablist\" aria-label=\"Variations\">\n          <button class=\"btn secondary\" role=\"tab\" aria-selected=\"true\" data-variant=\"base\">Classic Shoyu<\/button>\n          <button class=\"btn secondary\" role=\"tab\" aria-selected=\"false\" data-variant=\"vegan\">\ud83e\udd55 Vegan<\/button>\n          <button class=\"btn secondary\" role=\"tab\" aria-selected=\"false\" data-variant=\"glutenfree\">\ud83c\udf3e Gluten-Free<\/button>\n          <button class=\"btn secondary\" role=\"tab\" aria-selected=\"false\" data-variant=\"lowcarb\">Low-Carb<\/button>\n        <\/div>\n        <div id=\"variantPanels\" style=\"margin-top:12px;\">\n          <div data-panel=\"base\">\n            <ul>\n              <li>Broth: chicken stock + dried shiitake + kombu for umami. Tare: soy sauce, mirin, a touch of sugar.<\/li>\n              <li>Toppings: soft-boiled egg, sliced chicken, scallions, nori, corn, bamboo shoots.<\/li>\n            <\/ul>\n          <\/div>\n          <div data-panel=\"vegan\" hidden>\n            <ul>\n              <li>Broth: kombu + dried shiitake dashi; add roasted mushrooms for depth.<\/li>\n              <li>Tare: tamari + mirin + white miso. Use vegan noodles and skip eggs.<\/li>\n              <li>Toppings: tofu, sweetcorn, scallions, chili oil, sesame.<\/li>\n            <\/ul>\n          <\/div>\n          <div data-panel=\"glutenfree\" hidden>\n            <ul>\n              <li>Use certified GF tamari instead of soy sauce, and GF ramen or rice noodles.<\/li>\n              <li>Check mirin\/sake labels for gluten-containing additives.<\/li>\n            <\/ul>\n          <\/div>\n          <div data-panel=\"lowcarb\" hidden>\n            <ul>\n              <li>Swap noodles for spiralized zucchini or shirataki noodles; reduce sugar in tare.<\/li>\n              <li>Boost protein with extra chicken or tofu.<\/li>\n            <\/ul>\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <section class=\"timers card\" aria-labelledby=\"timers-title\">\n        <h3 id=\"timers-title\">Built-in Timers<\/h3>\n        <p class=\"sub\">Allow browser notifications to get alerts when time is up.<\/p>\n        <div id=\"timers\"><\/div>\n        <div class=\"controls\">\n          <button class=\"btn\" onclick=\"addTimer('Broth simmer',45,0)\">+ Broth 45:00<\/button>\n          <button class=\"btn\" onclick=\"addTimer('Soft-boiled eggs',6,30)\">+ Eggs 6:30<\/button>\n          <button class=\"btn\" onclick=\"addTimer('Noodles',3,0)\">+ Noodles 3:00<\/button>\n        <\/div>\n      <\/section>\n\n      <section class=\"faq card\" aria-labelledby=\"faq-title\">\n        <h3 id=\"faq-title\">FAQ<\/h3>\n        <details><summary>Can I make the broth ahead?<\/summary><p>Yes. The broth keeps 4 days refrigerated or 3 months frozen. Reheat to a simmer before serving.<\/p><\/details>\n        <details><summary>Best noodles to use?<\/summary><p>Fresh alkaline ramen noodles are ideal. Dried ramen works\u2014cook 30\u201360 seconds less than package for bite.<\/p><\/details>\n        <details><summary>How do I get jammy eggs?<\/summary><p>Boil 6:30 minutes, plunge into ice water 5 minutes, peel under running water.<\/p><\/details>\n        <details><summary>Low-sodium tips?<\/summary><p>Use low-sodium stock, lighten tare with water, and season bowls individually.<\/p><\/details>\n      <\/section>\n\n      <section class=\"ratings card\" aria-labelledby=\"ratings-title\">\n        <h3 id=\"ratings-title\">Rate this Recipe<\/h3>\n        <div aria-label=\"Star rating\" role=\"radiogroup\" id=\"ratingGroup\" style=\"font-size:2rem;\">\n          <button class=\"btn ghost\" role=\"radio\" aria-checked=\"false\" data-stars=\"1\">\u2b50<\/button>\n          <button class=\"btn ghost\" role=\"radio\" aria-checked=\"false\" data-stars=\"2\">\u2b50\u2b50<\/button>\n          <button class=\"btn ghost\" role=\"radio\" aria-checked=\"false\" data-stars=\"3\">\u2b50\u2b50\u2b50<\/button>\n          <button class=\"btn ghost\" role=\"radio\" aria-checked=\"false\" data-stars=\"4\">\u2b50\u2b50\u2b50\u2b50<\/button>\n          <button class=\"btn ghost\" role=\"radio\" aria-checked=\"false\" data-stars=\"5\">\u2b50\u2b50\u2b50\u2b50\u2b50<\/button>\n        <\/div>\n        <p id=\"ratingThanks\" class=\"sub\" aria-live=\"polite\"><\/p>\n      <\/section>\n\n      <section class=\"notes card\" aria-labelledby=\"notes-title\">\n        <h3 id=\"notes-title\">Your Notes<\/h3>\n        <textarea id=\"userNotes\" rows=\"6\" style=\"width:100%;\" placeholder=\"Add your tweaks, timing, brand notes...\"><\/textarea>\n        <div class=\"controls\" style=\"margin-top:8px;\">\n          <button class=\"btn\" onclick=\"saveNotes()\">Save Notes<\/button>\n          <button class=\"btn secondary\" onclick=\"clearNotes()\">Clear<\/button>\n        <\/div>\n      <\/section>\n\n    <\/article>\n\n    <aside class=\"card sticky-card quick-card\" aria-label=\"Quick info sticky card\">\n      <h3>Quick Recipe Card<\/h3>\n      <ul>\n        <li><span>Calories\/serving<\/span><strong id=\"quickCal\">650<\/strong><\/li>\n        <li><span>Diet<\/span><span class=\"diet-icons\">\ud83e\udd5b \ud83d\udc13 \ud83c\udf3e (mods: \ud83e\udd55, \ud83c\udf3e)<\/span><\/li>\n        <li><span>Equipment<\/span><span id=\"equipList\">Pot, saucepan, sieve, timer<\/span><\/li>\n        <li><span>Allergens<\/span><span>Soy, gluten, egg (optional)<\/span><\/li>\n      <\/ul>\n      <div class=\"controls\" style=\"margin-top:10px;\">\n        <button class=\"btn\" onclick=\"shareRecipe()\">Share<\/button>\n        <button class=\"btn secondary\" onclick=\"window.print()\">Print<\/button>\n      <\/div>\n    <\/aside>\n  <\/main>\n\n  <section class=\"shopping-list card\" aria-labelledby=\"shopping-title\" style=\"margin: 0 24px;\">\n    <h2 id=\"shopping-title\">Shopping List<\/h2>\n    <div class=\"controls\">\n      <button class=\"btn secondary\" onclick=\"copyShoppingList()\">Copy<\/button>\n      <button class=\"btn secondary\" onclick=\"printShoppingList()\">Print<\/button>\n      <button class=\"btn\" onclick=\"clearShoppingList()\">Clear<\/button>\n    <\/div>\n    <ul id=\"shoppingItems\"><\/ul>\n  <\/section>\n\n  <section class=\"card\" style=\"margin: 16px 24px;\" aria-labelledby=\"extras-title\">\n    <h2 id=\"extras-title\">Chef\u2019s Tips, Pairings &#038; Context<\/h2>\n    <div class=\"grid-3\">\n      <div class=\"card\">\n        <h4>Chef\u2019s Tips<\/h4>\n        <ul>\n          <li>Roast chicken bones or wings 20 minutes at 220\u00b0C\/425\u00b0F before simmering for extra depth.<\/li>\n          <li>Keep noodles and broth separate until serving to avoid sogginess.<\/li>\n          <li>Finish bowls with a few drops of toasted sesame oil or chili oil for aroma.<\/li>\n        <\/ul>\n      <\/div>\n      <div class=\"card\">\n        <h4>Beverage Pairings<\/h4>\n        <ul>\n          <li>Light lager or pilsner<\/li>\n          <li>Cold green tea or barley tea (mugicha)<\/li>\n          <li>Junmai sake, lightly chilled<\/li>\n        <\/ul>\n      <\/div>\n      <aside class=\"card\" aria-label=\"Context\">\n        <h4>Context<\/h4>\n        <p>Shoyu ramen hails from Tokyo, known for its clear soy-based broth. This version nods to tradition while keeping prep weeknight-friendly.<\/p>\n      <\/aside>\n    <\/div>\n  <\/section>\n\n  <section class=\"card share\" style=\"margin: 16px 24px;\" aria-labelledby=\"share-title\">\n    <h2 id=\"share-title\">Share &#038; Related<\/h2>\n    <div class=\"controls\">\n      <a class=\"btn\" id=\"shareFacebook\" target=\"_blank\" rel=\"noopener\">Facebook<\/a>\n      <a class=\"btn\" id=\"sharePinterest\" target=\"_blank\" rel=\"noopener\">Pinterest<\/a>\n      <a class=\"btn\" id=\"shareInstagram\" target=\"_blank\" rel=\"noopener\">Instagram<\/a>\n    <\/div>\n\n    <h3>Related Recipes<\/h3>\n    <div class=\"carousel\" aria-label=\"Related recipes carousel\">\n      <article class=\"card\" style=\"min-width:260px;\">\n        <img decoding=\"async\" src=\"https:\/\/images.unsplash.com\/photo-1484723091739-30a097e8f929?q=80&#038;w=800&#038;auto=format&#038;fit=crop\" alt=\"Bowl of miso soup\" style=\"width:100%;height:160px;object-fit:cover;border-radius:12px;\" \/>\n        <h4>Miso Soup with Tofu<\/h4>\n        <p>Light, umami-rich starter.<\/p>\n      <\/article>\n      <article class=\"card\" style=\"min-width:260px;\">\n        <img decoding=\"async\" src=\"https:\/\/images.unsplash.com\/photo-1525755662778-989d0524087e?q=80&#038;w=800&#038;auto=format&#038;fit=crop\" alt=\"Fried rice in wok\" style=\"width:100%;height:160px;object-fit:cover;border-radius:12px;\" \/>\n        <h4>Chicken Fried Rice<\/h4>\n        <p>Quick, satisfying weeknight meal.<\/p>\n      <\/article>\n      <article class=\"card\" style=\"min-width:260px;\">\n        <img decoding=\"async\" src=\"https:\/\/images.unsplash.com\/photo-1506368083636-6defb67639d8?q=80&#038;w=800&#038;auto=format&#038;fit=crop\" alt=\"Gyoza dumplings\" style=\"width:100%;height:160px;object-fit:cover;border-radius:12px;\" \/>\n        <h4>Crispy Gyoza<\/h4>\n        <p>Golden-bottom dumplings with soy dip.<\/p>\n      <\/article>\n    <\/div>\n  <\/section>\n\n  <footer class=\"card\" style=\"margin: 16px 24px;\" role=\"contentinfo\">\n    <small>\u00a9 <span id=\"year\"><\/span> Homemade Ramen Interactive Recipe. For personal use. Equipment links may be affiliate placeholders.<\/small>\n  <\/footer>\n\n  <script>\n    const state = {\n      unit: 'metric',\n      servings: 4,\n      baseServings: 4,\n      ingredients: [\n        { section: 'Broth & Aromatics', items: [\n          { name: 'Chicken stock (low sodium)', qtyMetric: 1600, unitMetric: 'ml', info: 'Water OK; season with tare to taste', subs: 'Use vegetable stock for vegan.' },\n          { name: 'Kombu (dried kelp)', qtyMetric: 10, unitMetric: 'g', info: 'Adds umami', subs: 'Skip if unavailable; add 1 tsp mushroom powder.' },\n          { name: 'Dried shiitake', qtyMetric: 10, unitMetric: 'g', info: 'Soak to release flavor', subs: 'Use fresh shiitake (3\u20134) if needed.' },\n          { name: 'Onion, halved', qtyMetric: 1, unitMetric: 'pc', info: 'Char for depth', subs: 'Leek whites work too.' },\n          { name: 'Garlic cloves, smashed', qtyMetric: 4, unitMetric: 'pc', info: 'Fragrant base', subs: '1 tsp garlic powder in a pinch.' },\n          { name: 'Ginger, sliced', qtyMetric: 20, unitMetric: 'g', info: 'Bright heat', subs: '1\/2 tsp ground ginger if needed.' }\n        ]},\n        { section: 'Tare (Seasoning Sauce)', items: [\n          { name: 'Soy sauce', qtyMetric: 120, unitMetric: 'ml', info: 'Base seasoning', subs: 'Tamari (GF) or light soy.' },\n          { name: 'Mirin', qtyMetric: 60, unitMetric: 'ml', info: 'Sweetness & sheen', subs: '1 tbsp sugar + 3 tbsp water if no mirin.' },\n          { name: 'Sake (optional)', qtyMetric: 30, unitMetric: 'ml', info: 'Aromatic lift', subs: 'Dry sherry or omit.' },\n          { name: 'Sugar', qtyMetric: 8, unitMetric: 'g', info: 'Balances salt', subs: 'Honey or mirin to taste.' }\n        ]},\n        { section: 'Noodles & Toppings', items: [\n          { name: 'Ramen noodles (fresh)', qtyMetric: 600, unitMetric: 'g', info: '150 g per serving', subs: 'Use dried noodles; cook ~30s less.' },\n          { name: 'Eggs (for jammy egg)', qtyMetric: 4, unitMetric: 'pc', info: 'Soft-boiled', subs: 'Skip for vegan.' },\n          { name: 'Cooked chicken, sliced', qtyMetric: 300, unitMetric: 'g', info: 'Protein topping', subs: 'Tofu (vegan) or pork chashu.' },\n          { name: 'Scallions, thinly sliced', qtyMetric: 40, unitMetric: 'g', info: 'Fresh bite', subs: 'Chives or spring onions.' },\n          { name: 'Nori sheets, halved', qtyMetric: 2, unitMetric: 'pc', info: 'Aromatic garnish', subs: 'Skip or use toasted seaweed snacks.' },\n          { name: 'Corn kernels (optional)', qtyMetric: 120, unitMetric: 'g', info: 'Sweet pop', subs: 'Bamboo shoots or bean sprouts.' }\n        ]}\n      ],\n      steps: [\n        { title: 'Make umami broth base', img: 'https:\/\/images.unsplash.com\/photo-1496117192811-51e1e9a6cf9a?q=80&w=1200&auto=format&fit=crop', alt: 'Aromatics simmering in pot', tips: 'Do not boil vigorously after adding kombu to avoid bitterness.', details: 'In a large pot, combine stock, kombu, dried shiitake, onion, garlic, and ginger. Bring just to a simmer, then lower heat. Simmer gently 45 minutes. Remove kombu at 20 minutes.' },\n        { title: 'Make the tare', img: 'https:\/\/images.unsplash.com\/photo-1544033527-b192daee1f3a?q=80&w=1200&auto=format&fit=crop', alt: 'Small saucepan with soy-based sauce', tips: 'Taste and adjust salt\/sweetness; it should be assertive.', details: 'In a small saucepan, combine soy sauce, mirin, sake, and sugar. Warm until sugar dissolves (2\u20133 minutes). Keep warm.' },\n        { title: 'Cook jammy eggs', img: 'https:\/\/images.unsplash.com\/photo-1515542706656-8e6ef17a1521?q=80&w=1200&auto=format&fit=crop', alt: 'Soft-boiled eggs in ice bath', tips: 'Peel under running water to avoid tearing.', details: 'Boil eggs for 6 minutes 30 seconds, then chill in ice water 5 minutes. Peel and halve.' },\n        { title: 'Boil noodles', img: 'https:\/\/images.unsplash.com\/photo-1574484284002-952d92456975?q=80&w=1200&auto=format&fit=crop', alt: 'Ramen noodles boiling in pot', tips: 'Slight undercook; noodles continue to soften in hot broth.', details: 'Bring a large pot of water to a rolling boil. Cook noodles per package minus 30\u201360 seconds. Drain well.' },\n        { title: 'Assemble bowls', img: 'https:\/\/images.unsplash.com\/photo-1542442818-8b9b93b9ad6e?q=80&w=1200&auto=format&fit=crop', alt: 'Assembled bowl of ramen', tips: 'Adjust tare to taste per bowl.', details: 'Add 1\u20132 tbsp tare to each bowl. Ladle in hot broth (about 350 ml), add noodles, then top with chicken, egg, scallions, corn, and nori.' }\n      ],\n      nutrition: { calories: 650, protein: 35, carbs: 70, fat: 22 },\n      shoppingList: JSON.parse(localStorage.getItem('ramenShopping')||'[]')\n    };\n\n    \/\/ Accessibility toggles\n    const contrastBtn = document.getElementById('contrastToggle');\n    contrastBtn.addEventListener('click', () => {\n      const isHC = document.documentElement.getAttribute('data-theme') === 'high-contrast';\n      document.documentElement.setAttribute('data-theme', isHC ? 'default' : 'high-contrast');\n      contrastBtn.setAttribute('aria-pressed', String(!isHC));\n    });\n    document.getElementById('printBtn').addEventListener('click', ()=>window.print());\n\n    \/\/ Populate ingredients\n    function formatQty(qty, unit) {\n      if (state.unit === 'imperial') {\n        switch(unit){\n          case 'ml': {\n            const cups = qty\/240; return cups < 1 ? (qty\/30).toFixed(1) + ' tbsp' : cups.toFixed(2) + ' cup' + (cups>=2?'s':'');\n          }\n          case 'g': {\n            const oz = qty\/28.3495; return oz.toFixed(1) + ' oz';\n          }\n          case 'pc': return qty + '';\n          default: return qty + ' ' + unit;\n        }\n      } else {\n        return unit==='pc' ? qty+'' : (Math.round(qty*10)\/10) + ' ' + unit;\n      }\n    }\n\n    function scaledQty(item){\n      const perServing = item.qtyMetric \/ state.baseServings; \n      return perServing * state.servings;\n    }\n\n    const ingredientsContainer = document.getElementById('ingredientsList');\n    function renderIngredients(){\n      ingredientsContainer.innerHTML = '';\n      state.ingredients.forEach((group, gi) => {\n        const sec = document.createElement('section');\n        sec.className = 'card';\n        const h = document.createElement('h4'); h.textContent = group.section; sec.appendChild(h);\n        group.items.forEach((item, ii) => {\n          const qty = scaledQty(item);\n          const row = document.createElement('div'); row.className = 'ingredient';\n          row.innerHTML = `\n            <button class=\"checkbox\" role=\"checkbox\" aria-checked=\"false\" tabindex=\"0\"><\/button>\n            <div>\n              <div><strong>${formatQty(qty, item.unitMetric)}<\/strong> <span>${item.name}<\/span><\/div>\n              <div class=\"info\">${item.info}<\/div>\n            <\/div>\n            <div title=\"Substitutions\" class=\"sub-icon\" aria-label=\"See substitutions\" tabindex=\"0\">\u2139\ufe0f<\/div>\n          `;\n          const cb = row.querySelector('.checkbox');\n          cb.addEventListener('click', () => { cb.classList.toggle('checked'); cb.setAttribute('aria-checked', cb.classList.contains('checked')); updateProgress(); });\n          cb.addEventListener('keydown', (e)=>{ if(e.key==='Enter' || e.key===' '){ e.preventDefault(); cb.click(); }});\n          const info = row.querySelector('.sub-icon');\n          info.addEventListener('click', ()=>{\n            alert(`${item.name} substitutions:\\n\\n${item.subs}`);\n          });\n          info.addEventListener('keydown', (e)=>{ if(e.key==='Enter' || e.key===' '){ e.preventDefault(); info.click(); }});\n          row.dataset.group = gi; row.dataset.index = ii;\n          ingredientsContainer.appendChild(row);\n        });\n      });\n    }\n\n    \/\/ Steps rendering\n    const stepsContainer = document.getElementById('steps');\n    function renderSteps(){\n      stepsContainer.innerHTML = '';\n      state.steps.forEach((s, i)=>{\n        const wrap = document.createElement('section'); wrap.className = 'step';\n        wrap.innerHTML = `\n          <div class=\"step-number\">${i+1}<\/div>\n          <h4>${s.title}<\/h4>\n          <p>${s.details}<\/p>\n          <img decoding=\"async\" src=\"${s.img}\" alt=\"${s.alt}\" \/>\n          <div class=\"tips\">\n            <button class=\"collapsible\" aria-expanded=\"false\">Show tips<\/button>\n            <div class=\"collapse-content\" hidden>${s.tips}<\/div>\n          <\/div>\n          <div style=\"margin-top:8px;\">\n            <button class=\"btn secondary mark-step\">Mark complete<\/button>\n          <\/div>\n        `;\n        const collBtn = wrap.querySelector('.collapsible');\n        const content = wrap.querySelector('.collapse-content');\n        collBtn.addEventListener('click', ()=>{\n          const open = content.hasAttribute('hidden') ? false : true;\n          if(open){ content.setAttribute('hidden',''); collBtn.setAttribute('aria-expanded','false'); }\n          else { content.removeAttribute('hidden'); collBtn.setAttribute('aria-expanded','true'); }\n        });\n        wrap.querySelector('.mark-step').addEventListener('click', ()=>{\n          wrap.classList.toggle('done');\n          updateProgress();\n        });\n        stepsContainer.appendChild(wrap);\n      });\n    }\n\n    \/\/ Progress\n    function updateProgress(){\n      const ingChecks = [...document.querySelectorAll('.ingredient .checkbox')];\n      const ingDone = ingChecks.filter(el=>el.classList.contains('checked')).length;\n      const stepDone = [...document.querySelectorAll('.step.done')].length;\n      const total = ingChecks.length + state.steps.length;\n      const done = ingDone + stepDone;\n      const pct = total? Math.round(done\/total*100) : 0;\n      document.getElementById('progressBar').style.width = pct + '%';\n      document.getElementById('progressLabel').textContent = pct + '% complete';\n    }\n\n    \/\/ Unit and servings controls\n    const unitToggle = document.getElementById('unitToggle');\n    unitToggle.addEventListener('change', (e)=>{ state.unit = e.target.value; renderIngredients(); updateProgress(); });\n    const servingInput = document.getElementById('servingInput');\n    function syncServings(val){ state.servings = Math.max(1, Number(val)||1); document.getElementById('servings-label').textContent = state.servings; servingInput.value = state.servings; renderIngredients(); updateNutrition(); }\n    document.getElementById('servingMinus').addEventListener('click', ()=>syncServings(state.servings-1));\n    document.getElementById('servingPlus').addEventListener('click', ()=>syncServings(state.servings+1));\n    servingInput.addEventListener('change', (e)=>syncServings(e.target.value));\n\n    \/\/ Add to shopping list\n    document.getElementById('addSelectedToList').addEventListener('click', ()=>{\n      const items = [...document.querySelectorAll('.ingredient')].filter(row=>row.querySelector('.checkbox').classList.contains('checked')).map(row=>{\n        const gi = Number(row.dataset.group), ii = Number(row.dataset.index);\n        const item = state.ingredients[gi].items[ii];\n        const qty = formatQty(scaledQty(item), item.unitMetric);\n        return qty + ' ' + item.name;\n      });\n      state.shoppingList = [...state.shoppingList, ...items];\n      localStorage.setItem('ramenShopping', JSON.stringify(state.shoppingList));\n      renderShopping();\n      alert('Added ' + items.length + ' item(s) to Shopping List');\n    });\n\n    function renderShopping(){\n      const ul = document.getElementById('shoppingItems');\n      ul.innerHTML = '';\n      state.shoppingList.forEach((txt, idx)=>{\n        const li = document.createElement('li');\n        li.textContent = txt;\n        li.style.padding = '6px 0';\n        const del = document.createElement('button'); del.textContent = 'Remove'; del.className = 'btn secondary'; del.style.marginLeft = '8px';\n        del.addEventListener('click', ()=>{ state.shoppingList.splice(idx,1); localStorage.setItem('ramenShopping', JSON.stringify(state.shoppingList)); renderShopping(); });\n        li.appendChild(del);\n        ul.appendChild(li);\n      });\n    }\n    function copyShoppingList(){\n      const text = state.shoppingList.join('\\n');\n      navigator.clipboard.writeText(text).then(()=>alert('Shopping list copied!'));\n    }\n    function printShoppingList(){\n      const w = window.open('', 'printwin');\n      w.document.write('<pre>' + state.shoppingList.map(x=>'- '+x).join('\\n') + '<\/pre>');\n      w.print();\n      w.close();\n    }\n    function clearShoppingList(){ if(confirm('Clear shopping list?')){ state.shoppingList = []; localStorage.setItem('ramenShopping','[]'); renderShopping(); } }\n\n    \/\/ Notes\n    const notesEl = document.getElementById('userNotes');\n    function saveNotes(){ localStorage.setItem('ramenNotes', notesEl.value); alert('Notes saved locally.'); }\n    function clearNotes(){ if(confirm('Clear notes?')){ notesEl.value=''; saveNotes(); } }\n    notesEl.value = localStorage.getItem('ramenNotes') || '';\n\n    \/\/ Ratings\n    const group = document.getElementById('ratingGroup');\n    const thanks = document.getElementById('ratingThanks');\n    ;[...group.querySelectorAll('button')].forEach(btn=>{\n      btn.addEventListener('click', ()=>{\n        const stars = Number(btn.dataset.stars);\n        localStorage.setItem('ramenRating', String(stars));\n        ;[...group.children].forEach(b=>b.setAttribute('aria-checked','false'));\n        btn.setAttribute('aria-checked','true');\n        thanks.textContent = `Thanks! You rated this ${stars} star${stars>1?'s':''}.`;\n      });\n    });\n    const savedRating = Number(localStorage.getItem('ramenRating')||'0');\n    if(savedRating){ const el = group.querySelector(`[data-stars=\"${savedRating}\"]`); if(el){ el.setAttribute('aria-checked','true'); thanks.textContent = `Your rating: ${savedRating}\u2b50`; } }\n\n    \/\/ Variations tabs\n    const variantBtns = document.querySelectorAll('[data-variant]');\n    const variantPanels = document.querySelectorAll('#variantPanels [data-panel]');\n    variantBtns.forEach(btn=>btn.addEventListener('click', ()=>{\n      variantBtns.forEach(b=>b.setAttribute('aria-selected','false'));\n      btn.setAttribute('aria-selected','true');\n      const key = btn.dataset.variant;\n      variantPanels.forEach(p=>{ p.hidden = p.getAttribute('data-panel')!==key; });\n    }));\n\n    \/\/ Timers\n    const timersWrap = document.getElementById('timers');\n    const timers = [];\n    function requestNotify(){ if('Notification' in window && Notification.permission==='default'){ Notification.requestPermission(); } }\n    function addTimer(label, m, s){\n      requestNotify();\n      const id = 't'+Date.now()+Math.random().toString(16).slice(2);\n      const timer = { id, label, total: m*60 + s, remaining: m*60 + s, running: false, interval: null };\n      timers.push(timer);\n      renderTimer(timer);\n    }\n    function renderTimer(t){\n      const el = document.createElement('div'); el.className = 'timer'; el.id = t.id;\n      el.innerHTML = `\n        <div>\n          <strong>${t.label}<\/strong>\n          <div class=\"sub\"><span class=\"time\" aria-live=\"polite\">${fmt(t.remaining)}<\/span><\/div>\n        <\/div>\n        <div class=\"timer-controls\">\n          <button class=\"btn\" aria-label=\"Start\">\u25b6<\/button>\n          <button class=\"btn secondary\" aria-label=\"Pause\">\u23f8<\/button>\n          <button class=\"btn secondary\" aria-label=\"Reset\">\u27f2<\/button>\n          <button class=\"btn ghost\" aria-label=\"Remove\">\u2716<\/button>\n        <\/div>\n      `;\n      const [start, pause, reset, remove] = el.querySelectorAll('button');\n      const timeEl = el.querySelector('.time');\n      function tick(){\n        t.remaining--; if(t.remaining<=0){\n          clearInterval(t.interval); t.running=false; timeEl.textContent='00:00';\n          try{ if('Notification' in window &#038;&#038; Notification.permission==='granted'){ new Notification(`${t.label} done!`); } }catch{}\n          alert(`${t.label} timer finished!`);\n        } else { timeEl.textContent = fmt(t.remaining); }\n      }\n      start.addEventListener('click', ()=>{ if(!t.running){ t.running=true; t.interval=setInterval(tick,1000); } });\n      pause.addEventListener('click', ()=>{ t.running=false; clearInterval(t.interval); });\n      reset.addEventListener('click', ()=>{ t.running=false; clearInterval(t.interval); t.remaining = t.total; timeEl.textContent = fmt(t.remaining); });\n      remove.addEventListener('click', ()=>{ t.running=false; clearInterval(t.interval); el.remove(); });\n      timersWrap.appendChild(el);\n    }\n    function fmt(sec){ const m = Math.floor(sec\/60), s = sec%60; return String(m).padStart(2,'0')+':'+String(s).padStart(2,'0'); }\n\n    \/\/ Video embed helpers\n    function ytIdFromUrl(url){\n      try{\n        const u = new URL(url);\n        if(u.hostname.includes('youtu.be')) return u.pathname.slice(1);\n        if(u.hostname.includes('youtube.com')) return u.searchParams.get('v');\n      }catch(e){ return ''; }\n      return '';\n    }\n    function setVideo(){\n      const url = document.getElementById('videoUrl').value.trim();\n      const id = ytIdFromUrl(url);\n      if(id){ document.getElementById('videoFrame').src = `https:\/\/www.youtube.com\/embed\/${id}`; }\n      else { alert('Please paste a valid YouTube URL'); }\n    }\n    function seekTo(seconds){\n      const ifr = document.getElementById('videoFrame');\n      if(!ifr.src) { alert('Embed a YouTube video first.'); return; }\n      const base = ifr.src.split('?')[0];\n      ifr.src = base + `?start=${Math.floor(seconds)}&autoplay=1`;\n    }\n\n    \/\/ Nutrition chart\n    let chart;\n    function renderChart(){\n      const ctx = document.getElementById('macroChart');\n      chart = new Chart(ctx, {\n        type: 'doughnut',\n        data: {\n          labels: ['Protein','Carbs','Fat'],\n          datasets: [{ data: [state.nutrition.protein, state.nutrition.carbs, state.nutrition.fat], backgroundColor: ['#60a5fa','#fbbf24','#f87171'] }]\n        },\n        options: { plugins: { legend: { position: 'bottom' } } }\n      });\n    }\n    function updateNutrition(){\n      const factor = state.servings \/ state.baseServings;\n      const cals = Math.round(650 * factor);\n      document.getElementById('calories').textContent = cals + ' kcal';\n      document.getElementById('quickCal').textContent = cals;\n      const p = Math.round(35 * factor); const c = Math.round(70 * factor); const f = Math.round(22 * factor);\n      document.getElementById('protein').textContent = p + ' g';\n      document.getElementById('carbs').textContent = c + ' g';\n      document.getElementById('fat').textContent = f + ' g';\n      if(chart){ chart.data.datasets[0].data = [p,c,f]; chart.update(); }\n    }\n\n    \/\/ Share links\n    function shareRecipe(){\n      const url = location.href;\n      if(navigator.share){ navigator.share({ title: 'Homemade Ramen', text: 'Check out this interactive ramen recipe!', url }); }\n      else { alert('Use the social buttons below to share.'); }\n    }\n    function setShareLinks(){\n      const url = encodeURIComponent(location.href);\n      const text = encodeURIComponent('Homemade Ramen \u2014 Interactive Recipe');\n      const img = encodeURIComponent(document.getElementById('heroImage').src);\n      document.getElementById('shareFacebook').href = `https:\/\/www.facebook.com\/sharer\/sharer.php?u=${url}`;\n      document.getElementById('sharePinterest').href = `https:\/\/pinterest.com\/pin\/create\/button\/?url=${url}&media=${img}&description=${text}`;\n      document.getElementById('shareInstagram').href = `https:\/\/www.instagram.com\/`;\n    }\n\n    \/\/ Density converter\n    function convertDensity(){\n      const ing = document.getElementById('densityIngredient').value;\n      const ml = Number(document.getElementById('densityMl').value||0);\n      const map = { water: 1, miso: 1.1, soy: 1.2, oil: 0.91 };\n      const g = ml * (map[ing]||1);\n      document.getElementById('densityG').value = Math.round(g) + ' g';\n    }\n\n    \/\/ Init\n    function init(){\n      renderIngredients();\n      renderSteps();\n      updateProgress();\n      renderChart();\n      updateNutrition();\n      renderShopping();\n      setShareLinks();\n      document.getElementById('year').textContent = new Date().getFullYear();\n    }\n    init();\n  <\/script>\n\n  <script type=\"application\/ld+json\">\n  {\n    \"@context\": \"https:\/\/schema.org\",\n    \"@type\": \"Recipe\",\n    \"name\": \"Homemade Ramen (Weeknight Shoyu)\",\n    \"description\": \"Deep, savory shoyu ramen in under 90 minutes with interactive tools.\",\n    \"image\": [\"https:\/\/images.unsplash.com\/photo-1542826438-bd32f43d626f?q=80&w=1600&auto=format&fit=crop\"],\n    \"recipeCategory\": \"Main course\",\n    \"recipeCuisine\": \"Japanese\",\n    \"keywords\": \"ramen, shoyu ramen, weeknight ramen, homemade ramen\",\n    \"prepTime\": \"PT25M\",\n    \"cookTime\": \"PT60M\",\n    \"totalTime\": \"PT85M\",\n    \"recipeYield\": \"4 servings\",\n    \"recipeIngredient\": [\n      \"1600 ml low-sodium chicken stock\",\n      \"10 g kombu\",\n      \"10 g dried shiitake\",\n      \"1 onion, halved\",\n      \"4 garlic cloves, smashed\",\n      \"20 g ginger, sliced\",\n      \"120 ml soy sauce\",\n      \"60 ml mirin\",\n      \"30 ml sake (optional)\",\n      \"8 g sugar\",\n      \"600 g fresh ramen noodles\",\n      \"4 eggs\",\n      \"300 g cooked chicken, sliced\",\n      \"40 g scallions, sliced\",\n      \"2 sheets nori\",\n      \"120 g corn (optional)\"\n    ],\n    \"recipeInstructions\": [\n      {\"@type\":\"HowToStep\",\"name\":\"Make umami broth base\",\"text\":\"Simmer stock with kombu, shiitake, onion, garlic, and ginger for 45 minutes. Remove kombu after 20 minutes.\"},\n      {\"@type\":\"HowToStep\",\"name\":\"Make the tare\",\"text\":\"Warm soy sauce, mirin, sake, and sugar until dissolved.\"},\n      {\"@type\":\"HowToStep\",\"name\":\"Cook jammy eggs\",\"text\":\"Boil eggs 6:30 minutes, chill 5 minutes, peel.\"},\n      {\"@type\":\"HowToStep\",\"name\":\"Boil noodles\",\"text\":\"Cook noodles just shy of package time; drain.\"},\n      {\"@type\":\"HowToStep\",\"name\":\"Assemble bowls\",\"text\":\"Add tare to bowls, ladle broth, add noodles and toppings.\"}\n    ],\n    \"nutrition\": {\"@type\":\"NutritionInformation\",\"calories\":\"650 calories\",\"proteinContent\":\"35 g\",\"carbohydrateContent\":\"70 g\",\"fatContent\":\"22 g\",\"fiberContent\":\"5 g\",\"sodiumContent\":\"2400 mg\"}\n  }\n  <\/script>\n<\/body>\n<\/html>\n\n\n\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Authentic Homemade Ramen | Recipe<\/title>\n    <style>\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n\n        body {\n            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n            line-height: 1.6;\n            color: #333;\n            background: #f8f9fa;\n        }\n\n        .hero {\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n            color: white;\n            padding: 60px 20px;\n            text-align: center;\n            position: relative;\n        }\n\n        .hero h1 {\n            font-size: 3.5em;\n            margin-bottom: 15px;\n            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);\n        }\n\n        .hero p {\n            font-size: 1.3em;\n            margin-bottom: 30px;\n            opacity: 0.95;\n        }\n\n        .hero-image {\n            max-width: 800px;\n            height: 400px;\n            margin: 30px auto;\n            background: linear-gradient(45deg, #f39c12, #e74c3c);\n            border-radius: 15px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-size: 4em;\n            box-shadow: 0 10px 30px rgba(0,0,0,0.3);\n        }\n\n        .badges {\n            display: flex;\n            justify-content: center;\n            gap: 20px;\n            flex-wrap: wrap;\n            margin-top: 20px;\n        }\n\n        .badge {\n            background: rgba(255,255,255,0.2);\n            backdrop-filter: blur(10px);\n            padding: 12px 25px;\n            border-radius: 25px;\n            font-weight: bold;\n            border: 2px solid rgba(255,255,255,0.3);\n        }\n\n        .container {\n            max-width: 1200px;\n            margin: 0 auto;\n            padding: 40px 20px;\n            display: grid;\n            grid-template-columns: 1fr 300px;\n            gap: 30px;\n        }\n\n        .main-content {\n            background: white;\n            padding: 40px;\n            border-radius: 15px;\n            box-shadow: 0 5px 20px rgba(0,0,0,0.1);\n        }\n\n        .sidebar {\n            position: sticky;\n            top: 20px;\n            height: fit-content;\n        }\n\n        .info-card {\n            background: white;\n            padding: 25px;\n            border-radius: 15px;\n            box-shadow: 0 5px 20px rgba(0,0,0,0.1);\n            margin-bottom: 20px;\n        }\n\n        .info-card h3 {\n            color: #667eea;\n            margin-bottom: 15px;\n            font-size: 1.3em;\n        }\n\n        .info-item {\n            padding: 10px 0;\n            border-bottom: 1px solid #eee;\n        }\n\n        .info-item:last-child {\n            border-bottom: none;\n        }\n\n        .dietary-tags {\n            display: flex;\n            gap: 10px;\n            flex-wrap: wrap;\n            margin-top: 15px;\n        }\n\n        .tag {\n            background: #e8f5e9;\n            color: #2e7d32;\n            padding: 5px 12px;\n            border-radius: 15px;\n            font-size: 0.9em;\n        }\n\n        .section {\n            margin: 40px 0;\n        }\n\n        .section h2 {\n            color: #667eea;\n            font-size: 2em;\n            margin-bottom: 20px;\n            padding-bottom: 10px;\n            border-bottom: 3px solid #667eea;\n        }\n\n        .unit-toggle {\n            background: #f0f0f0;\n            padding: 8px;\n            border-radius: 25px;\n            display: inline-flex;\n            gap: 5px;\n            margin-bottom: 20px;\n        }\n\n        .unit-btn {\n            padding: 8px 20px;\n            border: none;\n            background: transparent;\n            cursor: pointer;\n            border-radius: 20px;\n            font-weight: bold;\n            transition: all 0.3s;\n        }\n\n        .unit-btn.active {\n            background: #667eea;\n            color: white;\n        }\n\n        .ingredients-list {\n            list-style: none;\n            padding: 0;\n        }\n\n        .ingredient-item {\n            padding: 15px;\n            margin: 10px 0;\n            background: #f8f9fa;\n            border-radius: 10px;\n            display: flex;\n            align-items: center;\n            transition: all 0.3s;\n        }\n\n        .ingredient-item:hover {\n            background: #e9ecef;\n            transform: translateX(5px);\n        }\n\n        .ingredient-item input[type=\"checkbox\"] {\n            width: 20px;\n            height: 20px;\n            margin-right: 15px;\n            cursor: pointer;\n        }\n\n        .ingredient-item.checked {\n            opacity: 0.6;\n            text-decoration: line-through;\n        }\n\n        .steps {\n            counter-reset: step-counter;\n        }\n\n        .step {\n            margin: 30px 0;\n            padding: 25px;\n            background: #f8f9fa;\n            border-radius: 10px;\n            border-left: 5px solid #667eea;\n            position: relative;\n            counter-increment: step-counter;\n        }\n\n        .step::before {\n            content: counter(step-counter);\n            position: absolute;\n            left: -20px;\n            top: 20px;\n            background: #667eea;\n            color: white;\n            width: 40px;\n            height: 40px;\n            border-radius: 50%;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-weight: bold;\n            font-size: 1.2em;\n            box-shadow: 0 3px 10px rgba(0,0,0,0.2);\n        }\n\n        .step h3 {\n            color: #667eea;\n            margin-bottom: 10px;\n            margin-left: 30px;\n        }\n\n        .step p {\n            margin-left: 30px;\n            line-height: 1.8;\n        }\n\n        .tip {\n            background: #fff3cd;\n            padding: 12px;\n            border-radius: 8px;\n            margin: 10px 0 0 30px;\n            border-left: 3px solid #ffc107;\n            font-size: 0.95em;\n        }\n\n        .tip::before {\n            content: \"\ud83d\udca1 Tip: \";\n            font-weight: bold;\n        }\n\n        .nutrition-table {\n            width: 100%;\n            border-collapse: collapse;\n            margin: 20px 0;\n        }\n\n        .nutrition-table th,\n        .nutrition-table td {\n            padding: 15px;\n            text-align: left;\n            border-bottom: 1px solid #eee;\n        }\n\n        .nutrition-table th {\n            background: #667eea;\n            color: white;\n            font-weight: bold;\n        }\n\n        .nutrition-table tr:hover {\n            background: #f8f9fa;\n        }\n\n        .tabs {\n            display: flex;\n            gap: 10px;\n            margin-bottom: 20px;\n            flex-wrap: wrap;\n        }\n\n        .tab-btn {\n            padding: 12px 25px;\n            border: 2px solid #667eea;\n            background: white;\n            color: #667eea;\n            cursor: pointer;\n            border-radius: 25px;\n            font-weight: bold;\n            transition: all 0.3s;\n        }\n\n        .tab-btn:hover {\n            background: #f0f0f0;\n        }\n\n        .tab-btn.active {\n            background: #667eea;\n            color: white;\n        }\n\n        .tab-content {\n            display: none;\n            padding: 20px;\n            background: #f8f9fa;\n            border-radius: 10px;\n        }\n\n        .tab-content.active {\n            display: block;\n            animation: fadeIn 0.5s;\n        }\n\n        @keyframes fadeIn {\n            from { opacity: 0; }\n            to { opacity: 1; }\n        }\n\n        .tools {\n            display: flex;\n            gap: 15px;\n            flex-wrap: wrap;\n            margin: 30px 0;\n        }\n\n        .tool-btn {\n            padding: 15px 30px;\n            border: none;\n            border-radius: 30px;\n            font-weight: bold;\n            cursor: pointer;\n            transition: all 0.3s;\n            font-size: 1em;\n        }\n\n        .tool-btn.primary {\n            background: #667eea;\n            color: white;\n        }\n\n        .tool-btn.secondary {\n            background: #28a745;\n            color: white;\n        }\n\n        .tool-btn.tertiary {\n            background: #ffc107;\n            color: #333;\n        }\n\n        .tool-btn:hover {\n            transform: translateY(-2px);\n            box-shadow: 0 5px 15px rgba(0,0,0,0.2);\n        }\n\n        .serving-adjuster {\n            display: flex;\n            align-items: center;\n            gap: 15px;\n            padding: 20px;\n            background: #f8f9fa;\n            border-radius: 10px;\n            margin: 20px 0;\n        }\n\n        .serving-adjuster button {\n            width: 40px;\n            height: 40px;\n            border: none;\n            background: #667eea;\n            color: white;\n            border-radius: 50%;\n            font-size: 1.5em;\n            cursor: pointer;\n            transition: all 0.3s;\n        }\n\n        .serving-adjuster button:hover {\n            background: #5568d3;\n            transform: scale(1.1);\n        }\n\n        .serving-adjuster span {\n            font-size: 1.5em;\n            font-weight: bold;\n            min-width: 60px;\n            text-align: center;\n        }\n\n        .chef-tip {\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n            color: white;\n            padding: 30px;\n            border-radius: 15px;\n            margin: 30px 0;\n            box-shadow: 0 5px 20px rgba(0,0,0,0.2);\n        }\n\n        .chef-tip h3 {\n            font-size: 1.5em;\n            margin-bottom: 15px;\n        }\n\n        .chef-tip h3::before {\n            content: \"\ud83d\udc68\u200d\ud83c\udf73 \";\n        }\n\n        .related-recipes {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n            gap: 20px;\n            margin: 20px 0;\n        }\n\n        .recipe-card {\n            background: white;\n            padding: 20px;\n            border-radius: 10px;\n            box-shadow: 0 3px 10px rgba(0,0,0,0.1);\n            text-align: center;\n            transition: all 0.3s;\n        }\n\n        .recipe-card:hover {\n            transform: translateY(-5px);\n            box-shadow: 0 5px 20px rgba(0,0,0,0.2);\n        }\n\n        .recipe-card-icon {\n            font-size: 3em;\n            margin-bottom: 10px;\n        }\n\n        @media (max-width: 968px) {\n            .container {\n                grid-template-columns: 1fr;\n            }\n\n            .sidebar {\n                position: static;\n            }\n\n            .hero h1 {\n                font-size: 2.5em;\n            }\n\n            .main-content {\n                padding: 20px;\n            }\n        }\n\n        .progress-bar {\n            position: fixed;\n            top: 0;\n            left: 0;\n            height: 5px;\n            background: #667eea;\n            width: 0%;\n            z-index: 1000;\n            transition: width 0.1s;\n        }\n    <\/style>\n<\/head>\n<body>\n    <div class=\"progress-bar\" id=\"progressBar\"><\/div>\n\n    <div class=\"hero\">\n        <h1>\ud83c\udf5c Authentic Homemade Ramen<\/h1>\n        <p>Restaurant-quality Japanese noodle soup made from scratch in your own kitchen<\/p>\n        <div class=\"hero-image\">\ud83c\udf5c<\/div>\n        <div class=\"badges\">\n            <div class=\"badge\">\u23f1\ufe0f Prep: 30 min<\/div>\n            <div class=\"badge\">\ud83d\udd25 Cook: 4 hours<\/div>\n            <div class=\"badge\">\ud83d\udc68\u200d\ud83c\udf73 Intermediate<\/div>\n            <div class=\"badge\">\ud83c\udf7d\ufe0f Serves: <span id=\"heroServings\">4<\/span><\/div>\n        <\/div>\n    <\/div>\n\n    <div class=\"container\">\n        <div class=\"main-content\">\n            <div class=\"tools\">\n                <button class=\"tool-btn primary\" onclick=\"window.print()\">\ud83d\udda8\ufe0f Print Recipe<\/button>\n                <button class=\"tool-btn secondary\" onclick=\"addToShoppingList()\">\ud83d\uded2 Shopping List<\/button>\n                <button class=\"tool-btn tertiary\" onclick=\"shareRecipe()\">\ud83d\udce4 Share<\/button>\n            <\/div>\n\n            <div class=\"serving-adjuster\">\n                <label style=\"font-weight: bold;\">Servings:<\/label>\n                <button onclick=\"adjustServings(-1)\">\u2212<\/button>\n                <span id=\"servingCount\">4<\/span>\n                <button onclick=\"adjustServings(1)\">+<\/button>\n            <\/div>\n\n            <div class=\"section\">\n                <h2>Ingredients<\/h2>\n                <div class=\"unit-toggle\">\n                    <button class=\"unit-btn active\" onclick=\"toggleUnits('us')\">US<\/button>\n                    <button class=\"unit-btn\" onclick=\"toggleUnits('metric')\">Metric<\/button>\n                <\/div>\n\n                <h3>For the Broth:<\/h3>\n                <ul class=\"ingredients-list\" id=\"broth-ingredients\">\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong class=\"amount\" data-us=\"2\" data-metric=\"1\">2<\/strong> <span class=\"unit\" data-us=\"lbs\" data-metric=\"kg\">lbs<\/span> pork bones<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong class=\"amount\" data-us=\"1\" data-metric=\"500\">1<\/strong> <span class=\"unit\" data-us=\"lb\" data-metric=\"g\">lb<\/span> chicken carcass or wings<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong class=\"amount\" data-us=\"8\" data-metric=\"2\">8<\/strong> <span class=\"unit\" data-us=\"cups\" data-metric=\"L\">cups<\/span> water<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong>1<\/strong> onion, halved<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong>6<\/strong> cloves garlic, smashed<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong>1<\/strong> 3-inch piece fresh ginger, sliced<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong>2<\/strong> green onions<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong class=\"amount\" data-us=\"\u00bc\" data-metric=\"60\">\u00bc<\/strong> <span class=\"unit\" data-us=\"cup\" data-metric=\"ml\">cup<\/span> soy sauce<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong class=\"amount\" data-us=\"2\" data-metric=\"30\">2<\/strong> <span class=\"unit\" data-us=\"tbsp\" data-metric=\"ml\">tbsp<\/span> miso paste<\/span>\n                    <\/li>\n                <\/ul>\n\n                <h3>For the Noodles &#038; Toppings:<\/h3>\n                <ul class=\"ingredients-list\" id=\"toppings-ingredients\">\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong class=\"amount\" data-us=\"1\" data-metric=\"450\">1<\/strong> <span class=\"unit\" data-us=\"lb\" data-metric=\"g\">lb<\/span> fresh or dried ramen noodles<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong>4<\/strong> soft-boiled eggs (marinated in soy sauce)<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong class=\"amount\" data-us=\"\u00bd\" data-metric=\"225\">\u00bd<\/strong> <span class=\"unit\" data-us=\"lb\" data-metric=\"g\">lb<\/span> pork belly or chashu, sliced<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong>2<\/strong> cups bamboo shoots<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong>2<\/strong> sheets nori (seaweed)<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong>4<\/strong> green onions, sliced<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong>2<\/strong> cups fresh spinach or bok choy<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong class=\"amount\" data-us=\"1\" data-metric=\"15\">1<\/strong> <span class=\"unit\" data-us=\"tbsp\" data-metric=\"ml\">tbsp<\/span> sesame oil<\/span>\n                    <\/li>\n                    <li class=\"ingredient-item\">\n                        <input type=\"checkbox\" onchange=\"toggleIngredient(this)\">\n                        <span><strong>1<\/strong> tsp chili oil (optional)<\/span>\n                    <\/li>\n                <\/ul>\n            <\/div>\n\n            <div class=\"section\">\n                <h2>Cooking Instructions<\/h2>\n                <div class=\"steps\">\n                    <div class=\"step\">\n                        <h3>Prepare the Bones<\/h3>\n                        <p>Blanch pork bones and chicken in boiling water for 5 minutes to remove impurities. Drain and rinse under cold water to clean.<\/p>\n                        <div class=\"tip\">This step is crucial for a clear, flavorful broth without cloudiness.<\/div>\n                    <\/div>\n\n                    <div class=\"step\">\n                        <h3>Start the Broth<\/h3>\n                        <p>Place cleaned bones in a large pot with 8 cups of water. Add onion, garlic, ginger, and green onions. Bring to a boil, then reduce to a gentle simmer.<\/p>\n                        <div class=\"tip\">Skim off any foam that rises to the surface during the first 30 minutes.<\/div>\n                    <\/div>\n\n                    <div class=\"step\">\n                        <h3>Simmer Low and Slow<\/h3>\n                        <p>Cover and simmer for 4-6 hours, adding more water if needed. The longer you simmer, the richer the broth. Aim for a milky, cloudy appearance for tonkotsu-style.<\/p>\n                        <div class=\"tip\">A pressure cooker can reduce this time to 90 minutes for similar results!<\/div>\n                    <\/div>\n\n                    <div class=\"step\">\n                        <h3>Season the Broth<\/h3>\n                        <p>Strain the broth through a fine-mesh sieve. Return to pot and stir in soy sauce and miso paste. Adjust seasoning with salt to taste.<\/p>\n                    <\/div>\n\n                    <div class=\"step\">\n                        <h3>Prepare the Toppings<\/h3>\n                        <p>While broth simmers, prepare toppings: Soft-boil eggs (6.5 minutes), marinate in soy sauce. Pan-fry or braise pork belly until caramelized. Blanch spinach.<\/p>\n                        <div class=\"tip\">Marinate eggs for at least 2 hours or overnight for deeper flavor.<\/div>\n                    <\/div>\n\n                    <div class=\"step\">\n                        <h3>Cook the Noodles<\/h3>\n                        <p>Bring a large pot of water to a rolling boil. Cook ramen noodles according to package directions (usually 2-4 minutes). Drain immediately.<\/p>\n                        <div class=\"tip\">Don&#8217;t overcook! Ramen should be chewy (al dente).<\/div>\n                    <\/div>\n\n                    <div class=\"step\">\n                        <h3>Assemble Your Bowl<\/h3>\n                        <p>Divide noodles among 4 large bowls. Ladle hot broth over noodles. Arrange sliced pork, halved egg, bamboo shoots, nori, green onions, and greens on top.<\/p>\n                    <\/div>\n\n                    <div class=\"step\">\n                        <h3>Final Touch<\/h3>\n                        <p>Drizzle with sesame oil and chili oil if desired. Serve immediately while piping hot!<\/p>\n                        <div class=\"tip\">Serve with extra soy sauce, chili paste, and pickled ginger on the side.<\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n\n            <div class=\"chef-tip\">\n                <h3>Chef&#8217;s Secret Tip<\/h3>\n                <p>For ultra-rich, creamy tonkotsu-style broth, boil the bones at a vigorous rolling boil (not just a simmer) for the first 2 hours. This emulsifies the collagen and fat, creating that signature milky-white appearance. Also, save some broth fat to add back at serving for extra richness!<\/p>\n            <\/div>\n\n            <div class=\"section\">\n                <h2>Nutrition Facts<\/h2>\n                <p style=\"font-size: 0.9em; color: #666; margin-bottom: 10px;\">Per serving (1 bowl)<\/p>\n                <table class=\"nutrition-table\">\n                    <tr>\n                        <th>Nutrient<\/th>\n                        <th>Amount<\/th>\n                        <th>% Daily Value<\/th>\n                    <\/tr>\n                    <tr>\n                        <td>Calories<\/td>\n                        <td id=\"calories\">520<\/td>\n                        <td>26%<\/td>\n                    <\/tr>\n                    <tr>\n                        <td>Protein<\/td>\n                        <td id=\"protein\">32g<\/td>\n                        <td>64%<\/td>\n                    <\/tr>\n                    <tr>\n                        <td>Carbohydrates<\/td>\n                        <td id=\"carbs\">45g<\/td>\n                        <td>15%<\/td>\n                    <\/tr>\n                    <tr>\n                        <td>Fat<\/td>\n                        <td id=\"fat\">22g<\/td>\n                        <td>28%<\/td>\n                    <\/tr>\n                    <tr>\n                        <td>Fiber<\/td>\n                        <td>4g<\/td>\n                        <td>14%<\/td>\n                    <\/tr>\n                    <tr>\n                        <td>Sodium<\/td>\n                        <td>1850mg<\/td>\n                        <td>80%<\/td>\n                    <\/tr>\n                    <tr>\n                        <td>Vitamin A<\/td>\n                        <td>920 IU<\/td>\n                        <td>18%<\/td>\n                    <\/tr>\n                    <tr>\n                        <td>Vitamin C<\/td>\n                        <td>12mg<\/td>\n                        <td>20%<\/td>\n                    <\/tr>\n                    <tr>\n                        <td>Iron<\/td>\n                        <td>3.2mg<\/td>\n                        <td>18%<\/td>\n                    <\/tr>\n                <\/table>\n            <\/div>\n\n            <div class=\"section\">\n                <h2>Recipe Variations<\/h2>\n                <div class=\"tabs\">\n                    <button class=\"tab-btn active\" onclick=\"switchTab('vegan')\">\ud83e\udd55 Vegan<\/button>\n                    <button class=\"tab-btn\" onclick=\"switchTab('lowcarb')\">\ud83e\udd6c Low-Carb<\/button>\n                    <button class=\"tab-btn\" onclick=\"switchTab('spicy')\">\ud83c\udf36\ufe0f Spicy<\/button>\n                <\/div>\n\n                <div id=\"vegan\" class=\"tab-content active\">\n                    <h3>Vegan Ramen<\/h3>\n                    <ul>\n                        <li>Replace pork\/chicken broth with shiitake mushroom and kombu broth<\/li>\n                        <li>Use dried shiitake mushrooms (12-15) soaked overnight<\/li>\n                        <li>Add miso paste (white or red) for umami depth<\/li>\n                        <li>Top with marinated tofu, corn, bamboo shoots, and nori<\/li>\n                        <li>Use vegetable-based ramen noodles (check for egg-free)<\/li>\n                    <\/ul>\n                <\/div>\n\n                <div id=\"lowcarb\" class=\"tab-content\">\n                    <h3>Low-Carb Ramen<\/h3>\n                    <ul>\n                        <li>Replace ramen noodles with shirataki noodles or zucchini noodles<\/li>\n                        <li>Use the same rich broth recipe<\/li>\n                        <li>Double the protein: extra eggs and pork belly<\/li>\n                        <li>Add extra vegetables: bok choy, mushrooms, bean sprouts<\/li>\n                        <li>Reduces carbs from 45g to ~8g per serving<\/li>\n                    <\/ul>\n                <\/div>\n\n                <div id=\"spicy\" class=\"tab-content\">\n                    <h3>Spicy Miso Ramen<\/h3>\n                    <ul>\n                        <li>Add 2-3 tbsp of doubanjiang (spicy bean paste) to broth<\/li>\n                        <li>Include Korean gochugaru (chili flakes) for extra heat<\/li>\n                        <li>Top with spicy ground pork seasoned with chili oil<\/li>\n                        <li>Garnish with extra chili oil and fresh jalape\u00f1os<\/li>\n                        <li>Serve with kimchi on the side<\/li>\n                    <\/ul>\n                <\/div>\n            <\/div>\n\n            <div class=\"section\">\n                <h2>Storage &#038; Leftovers<\/h2>\n                <p style=\"padding: 20px; background: #f8f9fa; border-radius: 10px; line-height: 1.8;\">\n                    <strong>Broth:<\/strong> Store in airtight containers in the refrigerator for up to 5 days, or freeze for up to 3 months. Reheat gently on the stovetop.<br><br>\n                    <strong>Noodles:<\/strong> Best eaten fresh. If storing, toss with a bit of oil to prevent sticking. Reheat by dunking in boiling water for 30 seconds.<br><br>\n                    <strong>Toppings:<\/strong> Store separately in airtight containers for up to 3 days. Eggs should stay in their marinade.<br><br>\n                    <strong>Pro Tip:<\/strong> Make a large batch of broth and freeze in portions for quick weeknight ramen!\n                <\/p>\n            <\/div>\n\n            <div class=\"section\">\n                <h2>Beverage Pairing<\/h2>\n                <p style=\"padding: 20px; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; border-radius: 10px; line-height: 1.8;\">\n                    <strong>\ud83c\udf7a Beer:<\/strong> A crisp Japanese lager (Asahi, Sapporo) or light pilsner complements the rich broth perfectly.<br><br>\n                    <strong>\ud83c\udf76 Sake:<\/strong> Try a dry junmai sake served warm for a traditional pairing.<br><br>\n                    <strong>\ud83c\udf75 Tea:<\/strong> Green tea or barley tea (mugicha) provides a refreshing contrast to the savory flavors.<br><br>\n                    <strong>\ud83e\udd64 Non-Alcoholic:<\/strong> Sparkling water with lemon or a light citrus soda cleanses the palate between bites.\n                <\/p>\n            <\/div>\n\n            <div class=\"section\">\n                <h2>Related Recipes<\/h2>\n                <div class=\"related-recipes\">\n                    <div class=\"recipe-card\">\n                        <div class=\"recipe-card-icon\">\ud83c\udf72<\/div>\n                        <h4>Miso Soup<\/h4>\n                        <p>Classic Japanese starter<\/p>\n                    <\/div>\n                    <div class=\"recipe-card\">\n                        <div class=\"recipe-card-icon\">\ud83e\udd5f<\/div>\n                        <h4>Gyoza<\/h4>\n                        <p>Pan-fried dumplings<\/p>\n                    <\/div>\n                    <div class=\"recipe-card\">\n                        <div class=\"recipe-card-icon\">\ud83c\udf71<\/div>\n                        <h4>Chashu Pork<\/h4>\n                        <p>Braised pork belly<\/p>\n                    <\/div>\n                    <div class=\"recipe-card\">\n                        <div class=\"recipe-card-icon\">\ud83e\udd57<\/div>\n                        <h4>Cucumber Salad<\/h4>\n                        <p>Light &#038; refreshing side<\/p>\n                    <\/div>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <aside class=\"sidebar\">\n            <div class=\"info-card\">\n                <h3>Quick Info<\/h3>\n                <div class=\"info-item\">\n                    <strong>Total Time:<\/strong> 4.5 hours\n                <\/div>\n                <div class=\"info-item\">\n                    <strong>Active Time:<\/strong> 45 minutes\n                <\/div>\n                <div class=\"info-item\">\n                    <strong>Difficulty:<\/strong> Intermediate\n                <\/div>\n                <div class=\"info-item\">\n                    <strong>Servings:<\/strong> <span id=\"sidebarServings\">4<\/span>\n                <\/div>\n                <div class=\"info-item\">\n                    <strong>Calories per Bowl:<\/strong> <span id=\"sidebarCalories\">520<\/span>\n                <\/div>\n                <div class=\"dietary-tags\">\n                    <span class=\"tag\">High Protein<\/span>\n                    <span class=\"tag\">Comfort Food<\/span>\n                <\/div>\n            <\/div>\n\n            <div class=\"info-card\">\n                <h3>Equipment Needed<\/h3>\n                <ul style=\"padding-left: 20px;\">\n                    <li>Large stockpot (8+ quart)<\/li>\n                    <li>Fine-mesh strainer<\/li>\n                    <li>Large pot for noodles<\/li>\n                    <li>Sharp knife<\/li>\n                    <li>Cutting board<\/li>\n                    <li>Mixing bowls<\/li>\n                    <li>Ladle<\/li>\n                    <li>Tongs<\/li>\n                <\/ul>\n            <\/div>\n\n            <div class=\"info-card\">\n                <h3>Skill Level<\/h3>\n                <p style=\"line-height: 1.8;\">This recipe requires intermediate skills including braising, broth-making, and timing coordination. Perfect for adventurous home cooks!<\/p>\n            <\/div>\n        <\/aside>\n    <\/div>\n\n    <script>\n        let currentUnit = 'us';\n        let baseServings = 4;\n\n        function toggleUnits(unit) {\n            currentUnit = unit;\n            document.querySelectorAll('.unit-btn').forEach(btn => {\n                btn.classList.remove('active');\n            });\n            event.target.classList.add('active');\n\n            document.querySelectorAll('.amount').forEach(el => {\n                el.textContent = el.dataset[unit];\n            });\n            document.querySelectorAll('.unit').forEach(el => {\n                el.textContent = el.dataset[unit];\n            });\n        }\n\n        function toggleIngredient(checkbox) {\n            const parent = checkbox.parentElement;\n            if (checkbox.checked) {\n                parent.classList.add('checked');\n            } else {\n                parent.classList.remove('checked');\n            }\n        }\n\n        function adjustServings(delta) {\n            const newServings = Math.max(1, Math.min(12, baseServings + delta));\n            if (newServings === baseServings) return;\n\n            const ratio = newServings \/ baseServings;\n            baseServings = newServings;\n\n            document.getElementById('servingCount').textContent = newServings;\n            document.getElementById('heroServings').textContent = newServings;\n            document.getElementById('sidebarServings').textContent = newServings;\n\n            document.querySelectorAll('.amount').forEach(el => {\n                const usValue = parseFloat(el.dataset.us);\n                const metricValue = parseFloat(el.dataset.metric);\n                \n                if (!isNaN(usValue)) {\n                    el.dataset.us = (usValue * ratio).toFixed(1);\n                }\n                if (!isNaN(metricValue)) {\n                    el.dataset.metric = (metricValue * ratio).toFixed(1);\n                }\n                \n                el.textContent = el.dataset[currentUnit];\n            });\n\n            const baseCalories = 520;\n            const newCalories = Math.round(baseCalories);\n            document.getElementById('calories').textContent = newCalories;\n            document.getElementById('sidebarCalories').textContent = newCalories;\n        }\n\n        function switchTab(tabName) {\n            document.querySelectorAll('.tab-btn').forEach(btn => {\n                btn.classList.remove('active');\n            });\n            event.target.classList.add('active');\n\n            document.querySelectorAll('.tab-content').forEach(content => {\n                content.classList.remove('active');\n            });\n            document.getElementById(tabName).classList.add('active');\n        }\n\n        function addToShoppingList() {\n            const ingredients = [];\n            document.querySelectorAll('.ingredient-item span').forEach(item => {\n                const checkbox = item.previousElementSibling;\n                if (!checkbox.checked) {\n                    ingredients.push(item.textContent.trim());\n                }\n            });\n            \n            alert('Shopping List Generated!\\n\\n' + ingredients.join('\\n') + '\\n\\n(In a real app, this would save to your device or email you the list!)');\n        }\n\n        function shareRecipe() {\n            if (navigator.share) {\n                navigator.share({\n                    title: 'Authentic Homemade Ramen Recipe',\n                    text: 'Check out this amazing homemade ramen recipe!',\n                    url: window.location.href\n                });\n            } else {\n                alert('Recipe URL copied to clipboard!\\n\\n(In a real app, this would copy the link or open sharing options)');\n            }\n        }\n\n        window.addEventListener('scroll', () => {\n            const scrollTop = window.scrollY;\n            const docHeight = document.documentElement.scrollHeight - window.innerHeight;\n            const scrollPercent = (scrollTop \/ docHeight) * 100;\n            document.getElementById('progressBar').style.width = scrollPercent + '%';\n        });\n    <\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>Homemade Ramen \u2014 Ultimate Interactive Recipe Skip to content Homemade Ramen (Weeknight Shoyu) Deep, savory broth in under 90 minutes with silky noodles and classic toppings. Interactive tools make it foolproof. \u23f1\ufe0f Prep: 25 min \ud83c\udf73 Cook: 60 min \ud83c\udf5c Serves: 4 \ud83c\udff7\ufe0f Difficulty: Medium \ud83c\udf13 High-contrast \ud83d\udda8\ufe0f Print Tip: Use high-contrast mode while cooking. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-1206","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/brianbaker.net\/blog\/wp-json\/wp\/v2\/posts\/1206","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/brianbaker.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/brianbaker.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/brianbaker.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/brianbaker.net\/blog\/wp-json\/wp\/v2\/comments?post=1206"}],"version-history":[{"count":2,"href":"https:\/\/brianbaker.net\/blog\/wp-json\/wp\/v2\/posts\/1206\/revisions"}],"predecessor-version":[{"id":1210,"href":"https:\/\/brianbaker.net\/blog\/wp-json\/wp\/v2\/posts\/1206\/revisions\/1210"}],"wp:attachment":[{"href":"https:\/\/brianbaker.net\/blog\/wp-json\/wp\/v2\/media?parent=1206"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/brianbaker.net\/blog\/wp-json\/wp\/v2\/categories?post=1206"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/brianbaker.net\/blog\/wp-json\/wp\/v2\/tags?post=1206"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}