{"id":4110,"date":"2026-05-10T17:53:29","date_gmt":"2026-05-10T17:53:29","guid":{"rendered":"https:\/\/primetoolhub.com\/?page_id=4110"},"modified":"2026-08-08T11:26:03","modified_gmt":"2026-08-08T11:26:03","slug":"free-offline-weight-loss-meal-planner","status":"publish","type":"page","link":"https:\/\/schoolict.net\/tools\/free-offline-weight-loss-meal-planner\/","title":{"rendered":"Free Offline Weight Loss Meal Planner"},"content":{"rendered":"<div class=\"pth-hero-section\">\n<div class=\"pth-hero-content\">\n<h2><b data-path-to-node=\"1,1,0\" data-index-in-node=\"0\">Your Complete Offline Fitness &amp; Nutrition Companion<\/b> \u2014 Free Meal Planning &amp; Weight Loss Calculators, Zero Data Required<\/h2>\n<p>Looking for a free offline weight loss meal planner? Calculate your daily calorie deficit, check your BMI, and auto-generate custom meal plans 100% securely in your browser.<\/p>\n<div id=\"pth-toc-placeholder\">\u00a0<\/div>\n<\/div>\n<div class=\"pth-hero-image\"><img decoding=\"async\" src=\"https:\/\/schoolict.net\/tools\/wp-content\/uploads\/2026\/05\/free-offline-weight-loss-meal-planner-800x447.jpeg\" alt=\"free offline weight loss meal planner\" width=\"450\" height=\"253\" data-no-lazy=\"1\" \/><\/div>\n<\/div>\n\n\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>Table of Contents<\/h2><nav><ul><li><a href=\"#\ud83d\udd34-what-this-planner-does-for-you\">\ud83d\udd34\u00a0What this planner does for you<\/a><\/li><li><a href=\"#\ud83d\udfe1-building-a-week-youll-actually-follow\">\ud83d\udfe1\u00a0Building a week you&#8217;ll actually follow<\/a><\/li><li><a href=\"#\ud83d\udfe2-the-guardrails-why-it-wont-let-you-crash-diet\">\ud83d\udfe2\u00a0The guardrails: why it won&#8217;t let you crash-diet<\/a><\/li><\/ul><\/nav><\/div>\n\n\n<!-- ================================================================\r\n  MEAL PLAN STUDIO v10.0  (PrimeToolHub #57)\r\n  Slug: free-offline-weight-loss-meal-planner\r\n  100% offline, self-contained (built-in food database, no external fetch).\r\n  Health-safe by design: safe calorie floor + deficit cap + non-shaming output.\r\n  Scoped to #pth-mealplanner-wrap. Woody HTML snippet - HTML + Vanilla JS (ES5) + scoped CSS.\r\n================================================================= -->\r\n\r\n<style>\r\n#pth-mealplanner-wrap *, #pth-mealplanner-wrap *::before, #pth-mealplanner-wrap *::after { box-sizing: border-box; min-width: 0; }\r\n#pth-mealplanner-wrap {\r\n  font-family: 'Inter', system-ui, -apple-system, \"Segoe UI\", sans-serif;\r\n  color: #1e293b; font-weight: 500; line-height: 1.6; max-width: 1480px; margin: 0 auto; padding: 0 20px;\r\n}\r\n#pth-mealplanner-wrap .pth-mp-topbar { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; background: #fff; border: 1px solid #e2e8f0; border-radius: 14px; padding: 16px 22px; margin-bottom: 16px; }\r\n#pth-mealplanner-wrap .pth-mp-brand { display: flex; align-items: center; gap: 14px; }\r\n#pth-mealplanner-wrap .pth-mp-icon { width: 46px; height: 46px; border-radius: 11px; background: #059669; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 24px; flex: 0 0 auto; }\r\n#pth-mealplanner-wrap .pth-mp-title { font-size: 1.4rem; font-weight: 800; color: #0f172a; margin: 0; }\r\n#pth-mealplanner-wrap .pth-mp-sub { font-size: 0.8rem; color: #334155; font-weight: 600; margin: 2px 0 0; }\r\n#pth-mealplanner-wrap .pth-mp-badges { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }\r\n#pth-mealplanner-wrap .pth-mp-badge { font-size: 0.72rem; font-weight: 800; padding: 5px 11px; border-radius: 20px; border: 1px solid #e2e8f0; color: #334155; background: #f8fafc; }\r\n#pth-mealplanner-wrap .pth-mp-badge.off { color: #047857; border-color: #a7f3d0; background: #ecfdf5; }\r\n#pth-mealplanner-wrap .pth-mp-badge.ver { color: #1d4ed8; border-color: #bfdbfe; background: #eff6ff; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-disc { font-size: 0.8rem; color: #78350f; background: #fffbeb; border: 1px solid #fde68a; border-radius: 10px; padding: 10px 14px; margin-bottom: 16px; }\r\n#pth-mealplanner-wrap .pth-mp-disc b { color: #78350f; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-tabs { display: flex; gap: 6px; background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 12px; padding: 5px; margin-bottom: 18px; flex-wrap: wrap; }\r\n#pth-mealplanner-wrap .pth-mp-tab { flex: 1 1 auto; min-width: 120px; padding: 11px 14px; border: none; cursor: pointer; border-radius: 8px; background: transparent; color: #334155; font-family: inherit; font-size: 0.9rem; font-weight: 700; }\r\n#pth-mealplanner-wrap .pth-mp-tab:hover { color: #0f172a; }\r\n#pth-mealplanner-wrap .pth-mp-tab.active { background: #059669; color: #fff; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-panel { display: none; }\r\n#pth-mealplanner-wrap .pth-mp-panel.active { display: block; }\r\n#pth-mealplanner-wrap .pth-mp-card { background: #fff; border: 1px solid #e2e8f0; border-radius: 14px; padding: 20px; margin-bottom: 16px; }\r\n#pth-mealplanner-wrap .pth-mp-ctitle { font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 1px; color: #047857; margin: 0 0 14px; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-setup { display: grid; grid-template-columns: 1.4fr 1fr; gap: 16px; align-items: start; }\r\n#pth-mealplanner-wrap.full .pth-mp-setup { grid-template-columns: 1fr; }\r\n#pth-mealplanner-wrap .pth-mp-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; }\r\n#pth-mealplanner-wrap .pth-mp-fg { display: flex; flex-direction: column; gap: 6px; }\r\n#pth-mealplanner-wrap .pth-mp-label { font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.5px; color: #334155; }\r\n#pth-mealplanner-wrap .pth-mp-input, #pth-mealplanner-wrap .pth-mp-select { width: 100%; border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px 12px; background: #f8fafc; font-family: inherit; font-size: 0.9rem; font-weight: 600; color: #1e293b; outline: none; }\r\n#pth-mealplanner-wrap .pth-mp-input:focus, #pth-mealplanner-wrap .pth-mp-select:focus { border-color: #059669; background: #fff; }\r\n#pth-mealplanner-wrap .pth-mp-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-btn { display: inline-flex; align-items: center; gap: 7px; padding: 10px 16px; border-radius: 8px; border: 1px solid transparent; font-family: inherit; font-size: 0.88rem; font-weight: 700; cursor: pointer; }\r\n#pth-mealplanner-wrap .pth-mp-btn:disabled { opacity: 0.5; cursor: not-allowed; }\r\n#pth-mealplanner-wrap .pth-mp-btn.sm { padding: 7px 12px; font-size: 0.8rem; }\r\n#pth-mealplanner-wrap .pth-mp-primary { background: #059669; color: #fff; }\r\n#pth-mealplanner-wrap .pth-mp-primary:hover:not(:disabled) { filter: brightness(0.94); }\r\n#pth-mealplanner-wrap .pth-mp-blue { background: #2563eb; color: #fff; }\r\n#pth-mealplanner-wrap .pth-mp-blue:hover:not(:disabled) { background: #1d4ed8; }\r\n#pth-mealplanner-wrap .pth-mp-ghost { background: #f8fafc; color: #334155; border-color: #e2e8f0; }\r\n#pth-mealplanner-wrap .pth-mp-ghost:hover:not(:disabled) { background: #f1f5f9; color: #0f172a; }\r\n#pth-mealplanner-wrap .pth-mp-danger { background: #fef2f2; color: #991b1b; border-color: #fecaca; }\r\n#pth-mealplanner-wrap .pth-mp-danger:hover:not(:disabled) { background: #fee2e2; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-result { border: 1px solid #e2e8f0; border-radius: 10px; background: #f8fafc; padding: 16px; }\r\n#pth-mealplanner-wrap .pth-mp-stat { display: flex; justify-content: space-between; align-items: baseline; padding: 8px 0; border-bottom: 1px dashed #e2e8f0; }\r\n#pth-mealplanner-wrap .pth-mp-stat:last-child { border-bottom: none; }\r\n#pth-mealplanner-wrap .pth-mp-stat span { font-size: 0.85rem; font-weight: 700; color: #334155; }\r\n#pth-mealplanner-wrap .pth-mp-stat b { font-size: 1.05rem; font-weight: 800; color: #0f172a; }\r\n#pth-mealplanner-wrap .pth-mp-stat b.big { font-size: 1.5rem; color: #047857; }\r\n#pth-mealplanner-wrap .pth-mp-warn { font-size: 0.82rem; color: #78350f; background: #fffbeb; border: 1px solid #fde68a; border-radius: 8px; padding: 10px 12px; margin-top: 12px; }\r\n#pth-mealplanner-wrap .pth-mp-note-neutral { font-size: 0.8rem; color: #334155; margin-top: 8px; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-presets { display: flex; gap: 8px; flex-wrap: wrap; }\r\n#pth-mealplanner-wrap .pth-mp-preset { font-size: 0.82rem; font-weight: 700; padding: 8px 13px; border-radius: 8px; cursor: pointer; border: 1px solid #e2e8f0; background: #f8fafc; color: #334155; font-family: inherit; }\r\n#pth-mealplanner-wrap .pth-mp-preset.on { background: #ecfdf5; border-color: #a7f3d0; color: #047857; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-week { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 14px; }\r\n#pth-mealplanner-wrap .pth-mp-day { border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; padding: 14px; }\r\n#pth-mealplanner-wrap .pth-mp-day-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }\r\n#pth-mealplanner-wrap .pth-mp-day-name { font-weight: 800; color: #0f172a; font-size: 1rem; }\r\n#pth-mealplanner-wrap .pth-mp-bar { height: 8px; border-radius: 6px; background: #e2e8f0; overflow: hidden; margin: 4px 0 10px; }\r\n#pth-mealplanner-wrap .pth-mp-bar > i { display: block; height: 100%; background: #059669; }\r\n#pth-mealplanner-wrap .pth-mp-bar.over > i { background: #dc2626; }\r\n#pth-mealplanner-wrap .pth-mp-meal { margin-bottom: 8px; }\r\n#pth-mealplanner-wrap .pth-mp-meal-h { display: flex; justify-content: space-between; align-items: center; font-size: 0.74rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.5px; color: #475569; margin-bottom: 4px; }\r\n#pth-mealplanner-wrap .pth-mp-food { display: flex; justify-content: space-between; align-items: center; gap: 6px; font-size: 0.84rem; padding: 4px 6px; border-radius: 6px; background: #f8fafc; margin-bottom: 3px; }\r\n#pth-mealplanner-wrap .pth-mp-food span { color: #334155; }\r\n#pth-mealplanner-wrap .pth-mp-food .cal { font-weight: 700; color: #0f172a; }\r\n#pth-mealplanner-wrap .pth-mp-x { cursor: pointer; border: none; background: transparent; color: #dc2626; font-weight: 800; font-size: 0.9rem; line-height: 1; padding: 2px 4px; }\r\n#pth-mealplanner-wrap .pth-mp-add { border: 1px dashed #cbd5e1; background: transparent; color: #059669; font-weight: 700; font-size: 0.76rem; cursor: pointer; border-radius: 6px; padding: 4px 8px; font-family: inherit; }\r\n#pth-mealplanner-wrap .pth-mp-day-foot { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }\r\n#pth-mealplanner-wrap .pth-mp-day-ex { display: flex; align-items: center; gap: 6px; margin-top: 8px; font-size: 0.76rem; color: #475569; }\r\n#pth-mealplanner-wrap .pth-mp-day-ex input { width: 70px; padding: 4px 6px; border: 1px solid #e2e8f0; border-radius: 6px; font-family: inherit; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-modal { position: fixed; inset: 0; background: rgba(15,23,42,0.5); z-index: 999998; display: none; align-items: flex-start; justify-content: center; padding: 40px 16px; overflow: auto; }\r\n#pth-mealplanner-wrap .pth-mp-modal.show { display: flex; }\r\n#pth-mealplanner-wrap .pth-mp-modal-box { background: #fff; border-radius: 14px; padding: 20px; width: 100%; max-width: 560px; }\r\n#pth-mealplanner-wrap .pth-mp-modal-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }\r\n#pth-mealplanner-wrap .pth-mp-modal-head h3 { margin: 0; font-size: 1.05rem; color: #0f172a; font-weight: 800; }\r\n#pth-mealplanner-wrap .pth-mp-chips { display: flex; gap: 6px; flex-wrap: wrap; margin: 10px 0; }\r\n#pth-mealplanner-wrap .pth-mp-chip { font-size: 0.76rem; font-weight: 700; padding: 5px 11px; border-radius: 20px; cursor: pointer; border: 1px solid #e2e8f0; background: #f8fafc; color: #475569; font-family: inherit; }\r\n#pth-mealplanner-wrap .pth-mp-chip.on { background: #ecfdf5; border-color: #a7f3d0; color: #047857; }\r\n#pth-mealplanner-wrap .pth-mp-list { max-height: 320px; overflow: auto; margin-top: 8px; }\r\n#pth-mealplanner-wrap .pth-mp-li { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 9px 10px; border: 1px solid #e2e8f0; border-radius: 8px; margin-bottom: 6px; }\r\n#pth-mealplanner-wrap .pth-mp-li .info { font-size: 0.85rem; color: #334155; }\r\n#pth-mealplanner-wrap .pth-mp-li .info b { color: #0f172a; display: block; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-plate { display: flex; gap: 20px; align-items: center; flex-wrap: wrap; }\r\n#pth-mealplanner-wrap .pth-mp-plate svg { flex: 0 0 auto; }\r\n#pth-mealplanner-wrap .pth-mp-legend { font-size: 0.9rem; color: #1e293b; }\r\n#pth-mealplanner-wrap .pth-mp-legend div { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }\r\n#pth-mealplanner-wrap .pth-mp-sw { width: 14px; height: 14px; border-radius: 3px; flex: 0 0 auto; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-chart { display: flex; align-items: flex-end; gap: 8px; height: 160px; padding-top: 10px; border-bottom: 2px solid #e2e8f0; }\r\n#pth-mealplanner-wrap .pth-mp-chart .col { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; }\r\n#pth-mealplanner-wrap .pth-mp-chart .col > i { width: 70%; background: #059669; border-radius: 4px 4px 0 0; display: block; }\r\n#pth-mealplanner-wrap .pth-mp-chart .col > i.over { background: #dc2626; }\r\n#pth-mealplanner-wrap .pth-mp-chart .col small { font-size: 0.68rem; color: #475569; margin-top: 4px; }\r\n\r\n#pth-mealplanner-wrap .pth-mp-toast { position: fixed; bottom: 22px; right: 22px; z-index: 999999; display: none; padding: 13px 18px; border-radius: 10px; color: #fff; font-family: 'Inter', system-ui, sans-serif; font-size: 0.9rem; font-weight: 700; box-shadow: 0 8px 24px rgba(15,23,42,0.2); max-width: 320px; }\r\n#pth-mealplanner-wrap .pth-mp-toast.show { display: block; }\r\n\r\n@media (max-width: 768px) {\r\n  #pth-mealplanner-wrap { padding: 0 10px; }\r\n  #pth-mealplanner-wrap .pth-mp-setup { grid-template-columns: 1fr; }\r\n  #pth-mealplanner-wrap .pth-mp-week { grid-template-columns: 1fr; }\r\n}\r\n@media print {\r\n  body * { visibility: hidden; }\r\n  #pth-mealplanner-wrap #pth-mp-print-area, #pth-mealplanner-wrap #pth-mp-print-area * { visibility: visible; }\r\n  #pth-mealplanner-wrap #pth-mp-print-area { position: absolute; left: 0; top: 0; width: 100%; }\r\n  #pth-mealplanner-wrap .pth-mp-topbar, #pth-mealplanner-wrap .pth-mp-tabs, #pth-mealplanner-wrap .pth-mp-add, #pth-mealplanner-wrap .pth-mp-x { display: none !important; }\r\n}\r\n<\/style>\r\n\r\n<div id=\"pth-mealplanner-wrap\">\r\n\r\n  <div class=\"pth-mp-topbar\">\r\n    <div class=\"pth-mp-brand\">\r\n      <div class=\"pth-mp-icon\" aria-hidden=\"true\">&#127869;<\/div>\r\n      <div>\r\n        <h2 class=\"pth-mp-title\">Meal Plan Studio<\/h2>\r\n        <p class=\"pth-mp-sub\">Calorie targets, a 7-day planner, macros &amp; smart grocery lists &mdash; fully offline<\/p>\r\n      <\/div>\r\n    <\/div>\r\n    <div class=\"pth-mp-badges\">\r\n      <span class=\"pth-mp-badge ver\">v10.0<\/span>\r\n      <span class=\"pth-mp-badge off\">&#128274; 100% Offline<\/span>\r\n      <button type=\"button\" class=\"pth-mp-btn pth-mp-ghost sm\" data-action=\"sample\">&#128161; Sample<\/button>\r\n      <button type=\"button\" class=\"pth-mp-btn pth-mp-ghost sm\" data-action=\"wide\">&#9906; Wide<\/button>\r\n      <button type=\"button\" class=\"pth-mp-btn pth-mp-danger sm\" data-action=\"reset\">&#128465;&#65039; Reset<\/button>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <div class=\"pth-mp-disc\">\r\n    <b>Note:<\/b> This is a planning tool for general education, not medical advice. Calorie and body figures use standard formulas and are estimates only. Talk to a doctor or registered dietitian before starting a weight-loss plan, especially if you are pregnant, under 18, or managing a health condition.\r\n  <\/div>\r\n\r\n  <div class=\"pth-mp-tabs\" role=\"tablist\">\r\n    <button type=\"button\" class=\"pth-mp-tab active\" data-tab=\"setup\" role=\"tab\">&#9881;&#65039; Setup &amp; Targets<\/button>\r\n    <button type=\"button\" class=\"pth-mp-tab\" data-tab=\"planner\" role=\"tab\">&#128197; 7-Day Planner<\/button>\r\n    <button type=\"button\" class=\"pth-mp-tab\" data-tab=\"grocery\" role=\"tab\">&#128722; Grocery &amp; Export<\/button>\r\n    <button type=\"button\" class=\"pth-mp-tab\" data-tab=\"insights\" role=\"tab\">&#128202; Insights<\/button>\r\n  <\/div>\r\n\r\n  <div id=\"pth-mp-print-area\">\r\n\r\n  <!-- ===== SETUP ===== -->\r\n  <div class=\"pth-mp-panel active\" data-panel=\"setup\">\r\n    <div class=\"pth-mp-setup\">\r\n      <div class=\"pth-mp-card\">\r\n        <p class=\"pth-mp-ctitle\">Body metrics<\/p>\r\n        <div class=\"pth-mp-grid\">\r\n          <div class=\"pth-mp-fg\"><label class=\"pth-mp-label\" for=\"mp-sex\">Sex<\/label>\r\n            <select id=\"mp-sex\" class=\"pth-mp-select\"><option value=\"female\">Female<\/option><option value=\"male\">Male<\/option><\/select><\/div>\r\n          <div class=\"pth-mp-fg\"><label class=\"pth-mp-label\" for=\"mp-age\">Age<\/label>\r\n            <input id=\"mp-age\" class=\"pth-mp-input\" type=\"number\" min=\"16\" max=\"100\" placeholder=\"years\"><\/div>\r\n          <div class=\"pth-mp-fg\"><label class=\"pth-mp-label\" for=\"mp-weight\">Weight (kg)<\/label>\r\n            <input id=\"mp-weight\" class=\"pth-mp-input\" type=\"number\" min=\"30\" max=\"300\" placeholder=\"kg\"><\/div>\r\n          <div class=\"pth-mp-fg\"><label class=\"pth-mp-label\" for=\"mp-height\">Height (cm)<\/label>\r\n            <input id=\"mp-height\" class=\"pth-mp-input\" type=\"number\" min=\"120\" max=\"230\" placeholder=\"cm\"><\/div>\r\n          <div class=\"pth-mp-fg\"><label class=\"pth-mp-label\" for=\"mp-activity\">Activity level<\/label>\r\n            <select id=\"mp-activity\" class=\"pth-mp-select\">\r\n              <option value=\"1.2\">Sedentary (desk work)<\/option>\r\n              <option value=\"1.375\">Light (1-3 days\/week)<\/option>\r\n              <option value=\"1.55\" selected>Moderate (3-5 days\/week)<\/option>\r\n              <option value=\"1.725\">Active (6-7 days\/week)<\/option>\r\n              <option value=\"1.9\">Very active (physical job)<\/option>\r\n            <\/select><\/div>\r\n          <div class=\"pth-mp-fg\"><label class=\"pth-mp-label\" for=\"mp-rate\">Weekly goal<\/label>\r\n            <select id=\"mp-rate\" class=\"pth-mp-select\">\r\n              <option value=\"0\">Maintain weight<\/option>\r\n              <option value=\"0.25\">Lose 0.25 kg \/ week (gentle)<\/option>\r\n              <option value=\"0.5\" selected>Lose 0.5 kg \/ week (standard)<\/option>\r\n              <option value=\"0.75\">Lose 0.75 kg \/ week (faster)<\/option>\r\n            <\/select><\/div>\r\n        <\/div>\r\n        <div style=\"margin-top:16px;\">\r\n          <button type=\"button\" class=\"pth-mp-btn pth-mp-primary\" data-action=\"calc\" style=\"width:100%;justify-content:center;padding:13px;\">&#9889; Calculate My Targets<\/button>\r\n        <\/div>\r\n        <div style=\"margin-top:18px;\">\r\n          <p class=\"pth-mp-ctitle\">Macro split<\/p>\r\n          <div class=\"pth-mp-presets\">\r\n            <button type=\"button\" class=\"pth-mp-preset on\" data-preset=\"balanced\">&#9878;&#65039; Balanced<\/button>\r\n            <button type=\"button\" class=\"pth-mp-preset\" data-preset=\"lowcarb\">&#129388; Low Carb<\/button>\r\n            <button type=\"button\" class=\"pth-mp-preset\" data-preset=\"keto\">&#129361; Keto<\/button>\r\n            <button type=\"button\" class=\"pth-mp-preset\" data-preset=\"protein\">&#128170; High Protein<\/button>\r\n          <\/div>\r\n        <\/div>\r\n      <\/div>\r\n\r\n      <div class=\"pth-mp-card\">\r\n        <p class=\"pth-mp-ctitle\">Your daily targets<\/p>\r\n        <div class=\"pth-mp-result\" id=\"mp-result\">\r\n          <p style=\"color:#334155;font-size:0.9rem;margin:0;\">Enter your details and press <b>Calculate My Targets<\/b> to see a safe daily calorie and macro target.<\/p>\r\n        <\/div>\r\n        <div class=\"pth-mp-warn\" id=\"mp-floor-warn\" style=\"display:none;\"><\/div>\r\n        <p class=\"pth-mp-note-neutral\" id=\"mp-bmi-note\"><\/p>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- ===== PLANNER ===== -->\r\n  <div class=\"pth-mp-panel\" data-panel=\"planner\">\r\n    <div class=\"pth-mp-card\">\r\n      <div style=\"display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;\">\r\n        <p class=\"pth-mp-ctitle\" style=\"margin:0;\">Weekly plan &middot; target <b id=\"mp-plan-target\">not set<\/b> kcal\/day<\/p>\r\n        <div style=\"display:flex;gap:8px;flex-wrap:wrap;\">\r\n          <button type=\"button\" class=\"pth-mp-btn pth-mp-ghost sm\" data-action=\"tpl-manage\">&#128190; Meal Templates<\/button>\r\n          <button type=\"button\" class=\"pth-mp-btn pth-mp-ghost sm\" data-action=\"clear-week\">&#128260; Clear Week<\/button>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n    <div class=\"pth-mp-week\" id=\"mp-week\"><\/div>\r\n  <\/div>\r\n\r\n  <!-- ===== GROCERY ===== -->\r\n  <div class=\"pth-mp-panel\" data-panel=\"grocery\">\r\n    <div class=\"pth-mp-card\">\r\n      <p class=\"pth-mp-ctitle\">Smart weekly grocery list<\/p>\r\n      <p style=\"color:#334155;font-size:0.88rem;margin:0 0 12px;\">Every food across all seven days, combined and totalled so you shop once.<\/p>\r\n      <div style=\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px;\">\r\n        <button type=\"button\" class=\"pth-mp-btn pth-mp-primary sm\" data-action=\"build-grocery\">&#128260; Build List<\/button>\r\n        <button type=\"button\" class=\"pth-mp-btn pth-mp-blue sm\" data-action=\"export-csv\">&#128229; Export CSV<\/button>\r\n        <button type=\"button\" class=\"pth-mp-btn pth-mp-ghost sm\" data-action=\"print\">&#128424;&#65039; Print \/ Save PDF<\/button>\r\n      <\/div>\r\n      <div id=\"mp-grocery\"><\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- ===== INSIGHTS ===== -->\r\n  <div class=\"pth-mp-panel\" data-panel=\"insights\">\r\n    <div class=\"pth-mp-card\">\r\n      <p class=\"pth-mp-ctitle\">The balanced-plate method<\/p>\r\n      <p style=\"color:#334155;font-size:0.88rem;margin:0 0 14px;\">Numbers help, but you do not need to count every calorie. Building each plate to these proportions keeps meals balanced on their own.<\/p>\r\n      <div class=\"pth-mp-plate\">\r\n        <svg width=\"150\" height=\"150\" viewBox=\"0 0 42 42\" role=\"img\" aria-label=\"Balanced plate: half vegetables, one quarter protein, one quarter carbs\">\r\n          <circle cx=\"21\" cy=\"21\" r=\"15.9\" fill=\"#fff\" stroke=\"#e2e8f0\" stroke-width=\"1\"><\/circle>\r\n          <circle cx=\"21\" cy=\"21\" r=\"15.9\" fill=\"transparent\" stroke=\"#059669\" stroke-width=\"10\" stroke-dasharray=\"50 50\" stroke-dashoffset=\"25\"><\/circle>\r\n          <circle cx=\"21\" cy=\"21\" r=\"15.9\" fill=\"transparent\" stroke=\"#2563eb\" stroke-width=\"10\" stroke-dasharray=\"25 75\" stroke-dashoffset=\"75\"><\/circle>\r\n          <circle cx=\"21\" cy=\"21\" r=\"15.9\" fill=\"transparent\" stroke=\"#d97706\" stroke-width=\"10\" stroke-dasharray=\"25 75\" stroke-dashoffset=\"50\"><\/circle>\r\n        <\/svg>\r\n        <div class=\"pth-mp-legend\">\r\n          <div><span class=\"pth-mp-sw\" style=\"background:#059669;\"><\/span> Half the plate: vegetables and fruit<\/div>\r\n          <div><span class=\"pth-mp-sw\" style=\"background:#2563eb;\"><\/span> One quarter: lean protein<\/div>\r\n          <div><span class=\"pth-mp-sw\" style=\"background:#d97706;\"><\/span> One quarter: whole-grain carbs<\/div>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n    <div class=\"pth-mp-card\">\r\n      <p class=\"pth-mp-ctitle\">Calories across the week<\/p>\r\n      <div class=\"pth-mp-chart\" id=\"mp-chart\"><\/div>\r\n      <p style=\"color:#334155;font-size:0.82rem;margin:12px 0 0;\">Green bars sit at or under your daily target; red means the day went over. A day or two over is normal &mdash; look at the week as a whole.<\/p>\r\n    <\/div>\r\n    <div class=\"pth-mp-card\">\r\n      <p class=\"pth-mp-ctitle\">Gentle projection<\/p>\r\n      <div id=\"mp-projection\" style=\"color:#1e293b;font-size:0.92rem;\"><\/div>\r\n      <p class=\"pth-mp-note-neutral\">Steady, slow change is easier to keep off. This projection assumes a safe pace and never plans below a healthy minimum intake.<\/p>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <\/div><!-- \/print-area -->\r\n\r\n  <!-- FOOD PICKER MODAL -->\r\n  <div class=\"pth-mp-modal\" id=\"mp-modal\">\r\n    <div class=\"pth-mp-modal-box\">\r\n      <div class=\"pth-mp-modal-head\">\r\n        <h3 id=\"mp-modal-title\">Add food<\/h3>\r\n        <button type=\"button\" class=\"pth-mp-btn pth-mp-ghost sm\" data-action=\"modal-close\">&#10005;<\/button>\r\n      <\/div>\r\n      <input id=\"mp-search\" class=\"pth-mp-input\" type=\"text\" placeholder=\"Search foods...\">\r\n      <div class=\"pth-mp-chips\" id=\"mp-diet-chips\">\r\n        <button type=\"button\" class=\"pth-mp-chip on\" data-diet=\"all\">All<\/button>\r\n        <button type=\"button\" class=\"pth-mp-chip\" data-diet=\"veg\">Vegetarian<\/button>\r\n        <button type=\"button\" class=\"pth-mp-chip\" data-diet=\"vegan\">Vegan<\/button>\r\n        <button type=\"button\" class=\"pth-mp-chip\" data-diet=\"halal\">Halal<\/button>\r\n        <button type=\"button\" class=\"pth-mp-chip\" data-diet=\"glutenfree\">Gluten-free<\/button>\r\n        <button type=\"button\" class=\"pth-mp-chip\" data-diet=\"dairyfree\">Dairy-free<\/button>\r\n      <\/div>\r\n      <div class=\"pth-mp-list\" id=\"mp-food-list\"><\/div>\r\n      <div style=\"margin-top:10px;\">\r\n        <button type=\"button\" class=\"pth-mp-btn pth-mp-ghost sm\" data-action=\"custom-food\">&#43; Add custom food<\/button>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <div id=\"pth-mp-toast\" class=\"pth-mp-toast\" role=\"status\" aria-live=\"polite\"><\/div>\r\n<\/div>\r\n\r\n<script data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\">\r\n(function () {\r\n  'use strict';\r\n  if (window.pthMealPlannerApp) { return; }\r\n\r\n  var DAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];\r\n  var MEALS = ['breakfast', 'lunch', 'dinner', 'snacks'];\r\n  var MEAL_LABEL = { breakfast: 'Breakfast', lunch: 'Lunch', dinner: 'Dinner', snacks: 'Snacks' };\r\n  var STORE = 'pthMealPlanV10';\r\n\r\n  \/* Built-in food database. Values per listed serving (grams).\r\n     tags: dietary flags used by the filters. *\/\r\n  var FOODS = [\r\n    { name: 'Oatmeal, cooked', grams: 240, cal: 158, protein: 6, carbs: 27, fat: 3, tags: ['veg', 'vegan', 'halal', 'dairyfree'] },\r\n    { name: 'Greek yogurt, plain', grams: 170, cal: 100, protein: 17, carbs: 6, fat: 1, tags: ['veg', 'halal', 'glutenfree'] },\r\n    { name: 'Banana', grams: 118, cal: 105, protein: 1, carbs: 27, fat: 0, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Apple', grams: 182, cal: 95, protein: 0, carbs: 25, fat: 0, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Two boiled eggs', grams: 100, cal: 155, protein: 13, carbs: 1, fat: 11, tags: ['veg', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Whole-wheat toast, 2 slices', grams: 64, cal: 160, protein: 8, carbs: 28, fat: 2, tags: ['veg', 'vegan', 'halal', 'dairyfree'] },\r\n    { name: 'Grilled chicken breast', grams: 120, cal: 195, protein: 37, carbs: 0, fat: 4, tags: ['halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Baked salmon fillet', grams: 120, cal: 233, protein: 25, carbs: 0, fat: 14, tags: ['halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Canned tuna, drained', grams: 100, cal: 116, protein: 26, carbs: 0, fat: 1, tags: ['halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Lean beef mince, cooked', grams: 100, cal: 217, protein: 26, carbs: 0, fat: 12, tags: ['halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Bacon, 2 rashers', grams: 40, cal: 180, protein: 12, carbs: 0, fat: 14, tags: ['glutenfree', 'dairyfree'] },\r\n    { name: 'Tofu, firm', grams: 120, cal: 145, protein: 15, carbs: 4, fat: 9, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Cooked lentils', grams: 200, cal: 230, protein: 18, carbs: 40, fat: 1, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Chickpeas, cooked', grams: 160, cal: 269, protein: 15, carbs: 45, fat: 4, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Black beans, cooked', grams: 170, cal: 227, protein: 15, carbs: 41, fat: 1, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Brown rice, cooked', grams: 195, cal: 216, protein: 5, carbs: 45, fat: 2, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'White rice, cooked', grams: 186, cal: 205, protein: 4, carbs: 45, fat: 0, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Quinoa, cooked', grams: 185, cal: 222, protein: 8, carbs: 39, fat: 4, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Wholemeal pasta, cooked', grams: 140, cal: 174, protein: 7, carbs: 37, fat: 1, tags: ['veg', 'vegan', 'halal', 'dairyfree'] },\r\n    { name: 'Sweet potato, baked', grams: 130, cal: 112, protein: 2, carbs: 26, fat: 0, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Broccoli, steamed', grams: 156, cal: 55, protein: 4, carbs: 11, fat: 1, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Mixed green salad', grams: 100, cal: 20, protein: 1, carbs: 4, fat: 0, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Spinach, cooked', grams: 180, cal: 41, protein: 5, carbs: 7, fat: 0, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Avocado, half', grams: 100, cal: 160, protein: 2, carbs: 9, fat: 15, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Almonds, small handful', grams: 28, cal: 164, protein: 6, carbs: 6, fat: 14, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Peanut butter, 1 tbsp', grams: 16, cal: 94, protein: 4, carbs: 3, fat: 8, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Cheddar cheese', grams: 30, cal: 120, protein: 7, carbs: 0, fat: 10, tags: ['veg', 'halal', 'glutenfree'] },\r\n    { name: 'Milk, 1 cup', grams: 240, cal: 122, protein: 8, carbs: 12, fat: 5, tags: ['veg', 'halal', 'glutenfree'] },\r\n    { name: 'Cottage cheese', grams: 113, cal: 98, protein: 11, carbs: 3, fat: 4, tags: ['veg', 'halal', 'glutenfree'] },\r\n    { name: 'Hummus, 2 tbsp', grams: 30, cal: 70, protein: 2, carbs: 6, fat: 5, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Olive oil, 1 tbsp', grams: 14, cal: 119, protein: 0, carbs: 0, fat: 14, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Blueberries', grams: 148, cal: 84, protein: 1, carbs: 21, fat: 0, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Orange', grams: 131, cal: 62, protein: 1, carbs: 15, fat: 0, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Prawns, cooked', grams: 100, cal: 99, protein: 24, carbs: 0, fat: 1, tags: ['halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Turkey breast, sliced', grams: 100, cal: 135, protein: 30, carbs: 1, fat: 1, tags: ['halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Edamame', grams: 155, cal: 188, protein: 18, carbs: 14, fat: 8, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Cucumber slices', grams: 100, cal: 16, protein: 1, carbs: 4, fat: 0, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Carrot sticks', grams: 100, cal: 41, protein: 1, carbs: 10, fat: 0, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] },\r\n    { name: 'Dark chocolate, 1 square', grams: 10, cal: 60, protein: 1, carbs: 5, fat: 4, tags: ['veg', 'halal', 'glutenfree'] },\r\n    { name: 'Boiled potato', grams: 150, cal: 130, protein: 3, carbs: 30, fat: 0, tags: ['veg', 'vegan', 'halal', 'glutenfree', 'dairyfree'] }\r\n  ];\r\n\r\n  var PRESETS = {\r\n    balanced: { p: 0.30, c: 0.40, f: 0.30, label: 'Balanced' },\r\n    lowcarb: { p: 0.40, c: 0.20, f: 0.40, label: 'Low Carb' },\r\n    keto: { p: 0.25, c: 0.05, f: 0.70, label: 'Keto' },\r\n    protein: { p: 0.40, c: 0.35, f: 0.25, label: 'High Protein' }\r\n  };\r\n\r\n  var App = {\r\n    initialized: false,\r\n    root: null,\r\n    S: null,\r\n    preset: 'balanced',\r\n    diet: 'all',\r\n    target: { food: null, meal: null, day: null },\r\n\r\n    init: function () {\r\n      this.root = document.getElementById('pth-mealplanner-wrap');\r\n      if (!this.root) { return; }\r\n      this.load();\r\n      this.attach();\r\n      this.currentTab = 'setup';\r\n      this.renderWeek();\r\n      if (this.S.targetCals) { this.renderTargets(this.S); }\r\n    },\r\n\r\n    freshState: function () {\r\n      var days = [];\r\n      for (var i = 0; i < 7; i++) {\r\n        days.push({ ex: 0, meals: { breakfast: [], lunch: [], dinner: [], snacks: [] } });\r\n      }\r\n      return { sex: 'female', age: 0, weight: 0, height: 0, act: 1.55, rate: 0.5, tdee: 0, targetCals: 0, bmi: 0, days: days, templates: [] };\r\n    },\r\n\r\n    load: function () {\r\n      try {\r\n        var raw = localStorage.getItem(STORE);\r\n        this.S = raw ? JSON.parse(raw) : this.freshState();\r\n        if (!this.S.days || this.S.days.length !== 7) { this.S = this.freshState(); }\r\n        if (!this.S.templates) { this.S.templates = []; }\r\n      } catch (e) { this.S = this.freshState(); }\r\n    },\r\n\r\n    save: function () {\r\n      try { localStorage.setItem(STORE, JSON.stringify(this.S)); } catch (e) {}\r\n    },\r\n\r\n    attach: function () {\r\n      var self = this;\r\n      this.root.addEventListener('click', function (e) {\r\n        var el = e.target;\r\n        while (el && el !== self.root && !el.getAttribute('data-action') && !el.getAttribute('data-tab') && !el.getAttribute('data-preset') && !el.getAttribute('data-diet')) { el = el.parentNode; }\r\n        if (!el || el === self.root) { return; }\r\n        if (el.getAttribute('data-tab')) { self.switchTab(el.getAttribute('data-tab')); return; }\r\n        if (el.getAttribute('data-preset')) { self.setPreset(el.getAttribute('data-preset')); return; }\r\n        if (el.getAttribute('data-diet')) { self.setDiet(el.getAttribute('data-diet')); return; }\r\n        var a = el.getAttribute('data-action');\r\n        if (!a) { return; }\r\n        try { self.handle(a, el); } catch (err) { self.toast('Something went wrong. Please try again.', 'error'); if (window.console) { console.error('[MealPlan]', err); } }\r\n      });\r\n      this.root.addEventListener('input', function (e) {\r\n        if (e.target.id === 'mp-search') { self.renderFoodList(); }\r\n        if (e.target.getAttribute && e.target.getAttribute('data-exday') !== null && e.target.getAttribute('data-exday') !== undefined) {\r\n          var di = parseInt(e.target.getAttribute('data-exday'), 10);\r\n          if (!isNaN(di)) { self.S.days[di].ex = parseInt(e.target.value, 10) || 0; self.save(); self.updateDayTotals(di); }\r\n        }\r\n      });\r\n    },\r\n\r\n    handle: function (a, el) {\r\n      switch (a) {\r\n        case 'calc': this.calc(); break;\r\n        case 'sample': this.loadSample(); break;\r\n        case 'wide': this.root.className = this.root.className.indexOf('full') > -1 ? '' : 'full'; break;\r\n        case 'reset': this.resetAll(); break;\r\n        case 'clear-week': this.clearWeek(); break;\r\n        case 'add-food': this.openModal(el); break;\r\n        case 'modal-close': this.closeModal(); break;\r\n        case 'custom-food': this.customFood(); break;\r\n        case 'del-food': this.delFood(el); break;\r\n        case 'tpl-manage': this.manageTemplates(); break;\r\n        case 'save-tpl': this.saveTemplate(el); break;\r\n        case 'build-grocery': this.buildGrocery(); break;\r\n        case 'export-csv': this.exportCSV(); break;\r\n        case 'print': window.print(); break;\r\n      }\r\n    },\r\n\r\n    switchTab: function (name) {\r\n      this.currentTab = name;\r\n      var tabs = this.root.querySelectorAll('.pth-mp-tab'), i;\r\n      for (i = 0; i < tabs.length; i++) { tabs[i].className = 'pth-mp-tab' + (tabs[i].getAttribute('data-tab') === name ? ' active' : ''); }\r\n      var panels = this.root.querySelectorAll('.pth-mp-panel');\r\n      for (i = 0; i < panels.length; i++) { panels[i].className = 'pth-mp-panel' + (panels[i].getAttribute('data-panel') === name ? ' active' : ''); }\r\n      if (name === 'insights') { this.renderInsights(); }\r\n      if (name === 'grocery') { this.buildGrocery(); }\r\n    },\r\n\r\n    \/* ---------- targets with SAFE FLOOR ---------- *\/\r\n    num: function (id) { var e = document.getElementById(id); return e ? (parseFloat(e.value) || 0) : 0; },\r\n    valOf: function (id) { var e = document.getElementById(id); return e ? e.value : ''; },\r\n\r\n    calc: function () {\r\n      var sex = this.valOf('mp-sex');\r\n      var age = this.num('mp-age'), weight = this.num('mp-weight'), height = this.num('mp-height');\r\n      var act = parseFloat(this.valOf('mp-activity')) || 1.55;\r\n      var rate = parseFloat(this.valOf('mp-rate'));\r\n      if (!age || !weight || !height) { this.toast('Please enter age, weight, and height.', 'error'); return; }\r\n      if (age < 18) { this.toast('This tool is for adults. Please see a doctor for guidance under 18.', 'error'); return; }\r\n\r\n      var bmr = sex === 'male'\r\n        ? (10 * weight + 6.25 * height - 5 * age + 5)\r\n        : (10 * weight + 6.25 * height - 5 * age - 161);\r\n      var tdee = Math.round(bmr * act);\r\n\r\n      \/* 0.5 kg fat is about 3500 kcal, so weekly rate -> daily deficit *\/\r\n      var requestedDeficit = Math.round((rate * 7700) \/ 7);\r\n      \/* SAFE GUARDRAILS: cap the deficit and never plan below a healthy floor *\/\r\n      var deficitCap = Math.min(1000, Math.round(tdee * 0.25));\r\n      var deficit = Math.min(requestedDeficit, deficitCap);\r\n      var floor = sex === 'male' ? 1500 : 1200;\r\n      var rawTarget = tdee - deficit;\r\n      var targetCals = Math.max(floor, rawTarget);\r\n\r\n      var bmi = weight \/ Math.pow(height \/ 100, 2);\r\n\r\n      this.S.sex = sex; this.S.age = age; this.S.weight = weight; this.S.height = height;\r\n      this.S.act = act; this.S.rate = rate; this.S.tdee = tdee; this.S.targetCals = targetCals; this.S.bmi = bmi;\r\n      this.save();\r\n\r\n      this.renderTargets(this.S);\r\n\r\n      var warn = document.getElementById('mp-floor-warn');\r\n      var msgs = [];\r\n      if (requestedDeficit > deficitCap) { msgs.push('We eased your deficit to a safer level so the loss stays steady rather than rushed.'); }\r\n      if (rawTarget < floor) { msgs.push('Your target was raised to a healthy minimum of ' + floor + ' kcal. Eating below that is not advised without medical supervision.'); }\r\n      if (msgs.length) { warn.innerHTML = '&#9888;&#65039; ' + msgs.join(' '); warn.style.display = 'block'; }\r\n      else { warn.style.display = 'none'; }\r\n\r\n      var pt = document.getElementById('mp-plan-target');\r\n      if (pt) { pt.textContent = String(targetCals); }\r\n      this.toast('Targets calculated.', 'success');\r\n      this.renderWeek();\r\n    },\r\n\r\n    renderTargets: function (S) {\r\n      var res = document.getElementById('mp-result');\r\n      var pr = PRESETS[this.preset];\r\n      var kcal = S.targetCals;\r\n      var pG = Math.round((kcal * pr.p) \/ 4), cG = Math.round((kcal * pr.c) \/ 4), fG = Math.round((kcal * pr.f) \/ 9);\r\n      res.innerHTML = '';\r\n      res.appendChild(this.statRow('Maintenance (TDEE)', String(S.tdee) + ' kcal', false));\r\n      res.appendChild(this.statRow('Daily target', String(kcal) + ' kcal', true));\r\n      res.appendChild(this.statRow('Protein (' + pr.label + ')', String(pG) + ' g', false));\r\n      res.appendChild(this.statRow('Carbs', String(cG) + ' g', false));\r\n      res.appendChild(this.statRow('Fat', String(fG) + ' g', false));\r\n\r\n      var note = document.getElementById('mp-bmi-note');\r\n      if (S.bmi) {\r\n        var band = this.bmiBand(S.bmi);\r\n        note.textContent = 'For reference, your BMI is about ' + S.bmi.toFixed(1) + ' (' + band + '). BMI is a rough screen only and does not account for muscle, build, or health, so treat it as one data point, not a verdict.';\r\n      }\r\n    },\r\n\r\n    statRow: function (label, value, big) {\r\n      var d = document.createElement('div'); d.className = 'pth-mp-stat';\r\n      var s = document.createElement('span'); s.textContent = label;\r\n      var b = document.createElement('b'); b.textContent = value; if (big) { b.className = 'big'; }\r\n      d.appendChild(s); d.appendChild(b); return d;\r\n    },\r\n\r\n    bmiBand: function (bmi) {\r\n      if (bmi < 18.5) { return 'below the typical range'; }\r\n      if (bmi < 25) { return 'within the typical range'; }\r\n      if (bmi < 30) { return 'above the typical range'; }\r\n      return 'well above the typical range';\r\n    },\r\n\r\n    setPreset: function (p) {\r\n      this.preset = p;\r\n      var btns = this.root.querySelectorAll('.pth-mp-preset'), i;\r\n      for (i = 0; i < btns.length; i++) { btns[i].className = 'pth-mp-preset' + (btns[i].getAttribute('data-preset') === p ? ' on' : ''); }\r\n      if (this.S.targetCals) { this.renderTargets(this.S); }\r\n    },\r\n\r\n    \/* ---------- 7-day planner ---------- *\/\r\n    renderWeek: function () {\r\n      var host = document.getElementById('mp-week');\r\n      if (!host) { return; }\r\n      host.innerHTML = '';\r\n      for (var d = 0; d < 7; d++) { host.appendChild(this.buildDay(d)); }\r\n    },\r\n\r\n    buildDay: function (d) {\r\n      var self = this;\r\n      var day = this.S.days[d];\r\n      var card = document.createElement('div'); card.className = 'pth-mp-day';\r\n\r\n      var head = document.createElement('div'); head.className = 'pth-mp-day-head';\r\n      var nm = document.createElement('div'); nm.className = 'pth-mp-day-name'; nm.textContent = DAYS[d];\r\n      var tot = document.createElement('div'); tot.style.fontWeight = '800'; tot.style.fontSize = '0.85rem';\r\n      tot.id = 'mp-day-tot-' + d;\r\n      head.appendChild(nm); head.appendChild(tot);\r\n      card.appendChild(head);\r\n\r\n      var barWrap = document.createElement('div'); barWrap.className = 'pth-mp-bar'; barWrap.id = 'mp-day-bar-' + d;\r\n      var barI = document.createElement('i'); barWrap.appendChild(barI);\r\n      card.appendChild(barWrap);\r\n\r\n      for (var m = 0; m < MEALS.length; m++) {\r\n        var meal = MEALS[m];\r\n        var mb = document.createElement('div'); mb.className = 'pth-mp-meal';\r\n        var mh = document.createElement('div'); mh.className = 'pth-mp-meal-h';\r\n        var mspan = document.createElement('span'); mspan.textContent = MEAL_LABEL[meal];\r\n        var addb = document.createElement('button'); addb.type = 'button'; addb.className = 'pth-mp-add';\r\n        addb.setAttribute('data-action', 'add-food'); addb.setAttribute('data-day', String(d)); addb.setAttribute('data-meal', meal);\r\n        addb.textContent = '+ Add';\r\n        mh.appendChild(mspan); mh.appendChild(addb);\r\n        mb.appendChild(mh);\r\n        var items = day.meals[meal];\r\n        for (var f = 0; f < items.length; f++) { mb.appendChild(this.foodRow(d, meal, f, items[f])); }\r\n        card.appendChild(mb);\r\n      }\r\n\r\n      var ex = document.createElement('div'); ex.className = 'pth-mp-day-ex';\r\n      var exlbl = document.createElement('span'); exlbl.textContent = 'Exercise burned:';\r\n      var exin = document.createElement('input'); exin.type = 'number'; exin.min = '0'; exin.value = String(day.ex || 0);\r\n      exin.setAttribute('data-exday', String(d)); exin.setAttribute('aria-label', 'Exercise calories burned ' + DAYS[d]);\r\n      var exunit = document.createElement('span'); exunit.textContent = 'kcal';\r\n      ex.appendChild(exlbl); ex.appendChild(exin); ex.appendChild(exunit);\r\n      card.appendChild(ex);\r\n\r\n      var foot = document.createElement('div'); foot.className = 'pth-mp-day-foot';\r\n      var tplb = document.createElement('button'); tplb.type = 'button'; tplb.className = 'pth-mp-btn pth-mp-ghost sm';\r\n      tplb.setAttribute('data-action', 'save-tpl'); tplb.setAttribute('data-day', String(d)); tplb.textContent = 'Save as template';\r\n      foot.appendChild(tplb);\r\n      card.appendChild(foot);\r\n\r\n      setTimeout(function () { self.updateDayTotals(d); }, 0);\r\n      return card;\r\n    },\r\n\r\n    foodRow: function (d, meal, idx, item) {\r\n      var row = document.createElement('div'); row.className = 'pth-mp-food';\r\n      var left = document.createElement('span'); left.textContent = item.name;\r\n      var right = document.createElement('span'); right.style.display = 'flex'; right.style.alignItems = 'center'; right.style.gap = '6px';\r\n      var cal = document.createElement('span'); cal.className = 'cal'; cal.textContent = Math.round(item.cal) + ' kcal';\r\n      var x = document.createElement('button'); x.type = 'button'; x.className = 'pth-mp-x';\r\n      x.setAttribute('data-action', 'del-food'); x.setAttribute('data-day', String(d)); x.setAttribute('data-meal', meal); x.setAttribute('data-idx', String(idx));\r\n      x.textContent = '\\u00D7';\r\n      right.appendChild(cal); right.appendChild(x);\r\n      row.appendChild(left); row.appendChild(right);\r\n      return row;\r\n    },\r\n\r\n    updateDayTotals: function (d) {\r\n      var day = this.S.days[d];\r\n      var cal = 0, m, i;\r\n      for (m = 0; m < MEALS.length; m++) {\r\n        var items = day.meals[MEALS[m]];\r\n        for (i = 0; i < items.length; i++) { cal += items[i].cal; }\r\n      }\r\n      var net = cal - (day.ex || 0);\r\n      var totEl = document.getElementById('mp-day-tot-' + d);\r\n      var target = this.S.targetCals || 0;\r\n      if (totEl) {\r\n        totEl.textContent = Math.round(net) + (target ? ' \/ ' + target : '') + ' kcal';\r\n        totEl.style.color = (target && net > target) ? '#dc2626' : '#047857';\r\n      }\r\n      var barWrap = document.getElementById('mp-day-bar-' + d);\r\n      if (barWrap && target) {\r\n        var pct = Math.max(0, Math.min(100, (net \/ target) * 100));\r\n        barWrap.firstChild.style.width = pct + '%';\r\n        barWrap.className = 'pth-mp-bar' + (net > target ? ' over' : '');\r\n      }\r\n    },\r\n\r\n    \/* ---------- food modal ---------- *\/\r\n    openModal: function (el) {\r\n      this.target.day = parseInt(el.getAttribute('data-day'), 10);\r\n      this.target.meal = el.getAttribute('data-meal');\r\n      var t = document.getElementById('mp-modal-title');\r\n      t.textContent = 'Add to ' + DAYS[this.target.day] + ' - ' + MEAL_LABEL[this.target.meal];\r\n      document.getElementById('mp-search').value = '';\r\n      this.renderFoodList();\r\n      document.getElementById('mp-modal').className = 'pth-mp-modal show';\r\n    },\r\n    closeModal: function () { document.getElementById('mp-modal').className = 'pth-mp-modal'; },\r\n\r\n    setDiet: function (diet) {\r\n      this.diet = diet;\r\n      var chips = this.root.querySelectorAll('#mp-diet-chips .pth-mp-chip'), i;\r\n      for (i = 0; i < chips.length; i++) { chips[i].className = 'pth-mp-chip' + (chips[i].getAttribute('data-diet') === diet ? ' on' : ''); }\r\n      this.renderFoodList();\r\n    },\r\n\r\n    renderFoodList: function () {\r\n      var host = document.getElementById('mp-food-list');\r\n      if (!host) { return; }\r\n      var q = (document.getElementById('mp-search').value || '').toLowerCase();\r\n      host.innerHTML = '';\r\n      var self = this;\r\n      var shown = 0;\r\n      for (var i = 0; i < FOODS.length; i++) {\r\n        var fd = FOODS[i];\r\n        if (this.diet !== 'all' && (!fd.tags || fd.tags.indexOf(this.diet) === -1)) { continue; }\r\n        if (q && fd.name.toLowerCase().indexOf(q) === -1) { continue; }\r\n        host.appendChild(this.foodPickRow(fd));\r\n        shown++;\r\n      }\r\n      if (!shown) {\r\n        var p = document.createElement('p'); p.style.color = '#475569'; p.style.fontSize = '0.86rem';\r\n        p.textContent = 'No foods match. Try a different filter, or add a custom food below.';\r\n        host.appendChild(p);\r\n      }\r\n    },\r\n\r\n    foodPickRow: function (fd) {\r\n      var self = this;\r\n      var row = document.createElement('div'); row.className = 'pth-mp-li';\r\n      var info = document.createElement('div'); info.className = 'info';\r\n      var b = document.createElement('b'); b.textContent = fd.name; info.appendChild(b);\r\n      var meta = document.createElement('span');\r\n      meta.textContent = fd.grams + ' g - ' + fd.cal + ' kcal - P' + fd.protein + ' C' + fd.carbs + ' F' + fd.fat;\r\n      info.appendChild(meta);\r\n      var add = document.createElement('button'); add.type = 'button'; add.className = 'pth-mp-btn pth-mp-primary sm'; add.textContent = '+ Add';\r\n      add.onclick = function () { self.addFoodToTarget(fd); };\r\n      row.appendChild(info); row.appendChild(add);\r\n      return row;\r\n    },\r\n\r\n    addFoodToTarget: function (fd) {\r\n      if (this.target.day === null || this.target.day === undefined) { return; }\r\n      var copy = { name: fd.name, grams: fd.grams, cal: fd.cal, protein: fd.protein, carbs: fd.carbs, fat: fd.fat };\r\n      this.S.days[this.target.day].meals[this.target.meal].push(copy);\r\n      this.save();\r\n      this.refreshDay(this.target.day);\r\n      this.toast(fd.name + ' added.', 'success');\r\n    },\r\n\r\n    customFood: function () {\r\n      var name = window.prompt('Food name:');\r\n      if (!name) { return; }\r\n      var cal = parseInt(window.prompt('Calories (kcal):'), 10);\r\n      if (isNaN(cal) || cal < 0) { this.toast('Please enter a valid calorie number.', 'error'); return; }\r\n      var copy = { name: name, grams: 0, cal: cal, protein: 0, carbs: 0, fat: 0 };\r\n      this.S.days[this.target.day].meals[this.target.meal].push(copy);\r\n      this.save(); this.refreshDay(this.target.day); this.closeModal();\r\n      this.toast('Custom food added.', 'success');\r\n    },\r\n\r\n    delFood: function (el) {\r\n      var d = parseInt(el.getAttribute('data-day'), 10);\r\n      var meal = el.getAttribute('data-meal');\r\n      var idx = parseInt(el.getAttribute('data-idx'), 10);\r\n      this.S.days[d].meals[meal].splice(idx, 1);\r\n      this.save(); this.refreshDay(d);\r\n    },\r\n\r\n    refreshDay: function (d) {\r\n      var host = document.getElementById('mp-week');\r\n      if (!host || !host.children[d]) { this.renderWeek(); return; }\r\n      host.replaceChild(this.buildDay(d), host.children[d]);\r\n    },\r\n\r\n    \/* ---------- templates ---------- *\/\r\n    saveTemplate: function (el) {\r\n      var d = parseInt(el.getAttribute('data-day'), 10);\r\n      var day = this.S.days[d];\r\n      var count = 0, m;\r\n      for (m = 0; m < MEALS.length; m++) { count += day.meals[MEALS[m]].length; }\r\n      if (!count) { this.toast('That day is empty - add foods first.', 'error'); return; }\r\n      var name = window.prompt('Template name:', DAYS[d] + ' plan');\r\n      if (!name) { return; }\r\n      this.S.templates.push({ name: name, meals: JSON.parse(JSON.stringify(day.meals)) });\r\n      this.save();\r\n      this.toast('Template \"' + name + '\" saved.', 'success');\r\n    },\r\n\r\n    manageTemplates: function () {\r\n      if (!this.S.templates.length) { this.toast('No templates yet. Use \"Save as template\" on a day.', 'info'); return; }\r\n      var names = [];\r\n      for (var i = 0; i < this.S.templates.length; i++) { names.push((i + 1) + '. ' + this.S.templates[i].name); }\r\n      var pick = window.prompt('Apply which template to which day?\\n' + names.join('\\n') + '\\n\\nEnter: templateNumber,dayNumber (1=Mon ... 7=Sun)', '1,1');\r\n      if (!pick) { return; }\r\n      var parts = pick.split(',');\r\n      var ti = parseInt(parts[0], 10) - 1, di = parseInt(parts[1], 10) - 1;\r\n      if (isNaN(ti) || isNaN(di) || !this.S.templates[ti] || di < 0 || di > 6) { this.toast('Could not read that. Use the format templateNumber,dayNumber.', 'error'); return; }\r\n      this.S.days[di].meals = JSON.parse(JSON.stringify(this.S.templates[ti].meals));\r\n      this.save(); this.refreshDay(di);\r\n      this.toast('Applied to ' + DAYS[di] + '.', 'success');\r\n    },\r\n\r\n    \/* ---------- grocery ---------- *\/\r\n    buildGrocery: function () {\r\n      var host = document.getElementById('mp-grocery');\r\n      if (!host) { return; }\r\n      var map = {}, order = [];\r\n      for (var d = 0; d < 7; d++) {\r\n        for (var m = 0; m < MEALS.length; m++) {\r\n          var items = this.S.days[d].meals[MEALS[m]];\r\n          for (var i = 0; i < items.length; i++) {\r\n            var it = items[i];\r\n            if (!map[it.name]) { map[it.name] = { count: 0, grams: 0, cal: 0 }; order.push(it.name); }\r\n            map[it.name].count += 1; map[it.name].grams += (it.grams || 0); map[it.name].cal += it.cal;\r\n          }\r\n        }\r\n      }\r\n      host.innerHTML = '';\r\n      if (!order.length) { var p = document.createElement('p'); p.style.color = '#475569'; p.textContent = 'Add foods to your week, then build the list.'; host.appendChild(p); return; }\r\n      order.sort();\r\n      for (var k = 0; k < order.length; k++) {\r\n        var g = map[order[k]];\r\n        var row = document.createElement('div'); row.className = 'pth-mp-food'; row.style.padding = '8px 12px';\r\n        var left = document.createElement('span'); left.textContent = order[k] + '  x' + g.count;\r\n        var right = document.createElement('span'); right.className = 'cal';\r\n        right.textContent = (g.grams ? Math.round(g.grams) + ' g - ' : '') + Math.round(g.cal) + ' kcal';\r\n        row.appendChild(left); row.appendChild(right);\r\n        host.appendChild(row);\r\n      }\r\n    },\r\n\r\n    exportCSV: function () {\r\n      var rows = [['Day', 'Meal', 'Food', 'Grams', 'Calories', 'Protein', 'Carbs', 'Fat']];\r\n      for (var d = 0; d < 7; d++) {\r\n        for (var m = 0; m < MEALS.length; m++) {\r\n          var items = this.S.days[d].meals[MEALS[m]];\r\n          for (var i = 0; i < items.length; i++) {\r\n            var it = items[i];\r\n            rows.push([DAYS[d], MEAL_LABEL[MEALS[m]], it.name, it.grams, it.cal, it.protein, it.carbs, it.fat]);\r\n          }\r\n        }\r\n      }\r\n      if (rows.length === 1) { this.toast('Nothing to export yet.', 'error'); return; }\r\n      var csv = '';\r\n      for (var r = 0; r < rows.length; r++) {\r\n        var line = [];\r\n        for (var c = 0; c < rows[r].length; c++) { line.push('\"' + String(rows[r][c]).replace(\/\"\/g, '\"\"') + '\"'); }\r\n        csv += line.join(',') + '\\r\\n';\r\n      }\r\n      var blob = new Blob([csv], { type: 'text\/csv' });\r\n      var url = URL.createObjectURL(blob);\r\n      var a = document.createElement('a'); a.href = url; a.download = 'meal-plan.csv';\r\n      document.body.appendChild(a); a.click(); document.body.removeChild(a);\r\n      setTimeout(function () { URL.revokeObjectURL(url); }, 1500);\r\n      this.toast('CSV downloaded.', 'success');\r\n    },\r\n\r\n    \/* ---------- insights ---------- *\/\r\n    renderInsights: function () {\r\n      var chart = document.getElementById('mp-chart');\r\n      if (chart) {\r\n        chart.innerHTML = '';\r\n        var target = this.S.targetCals || 0;\r\n        var max = target ? target * 1.4 : 2500;\r\n        for (var d = 0; d < 7; d++) {\r\n          var cal = 0, m, i;\r\n          for (m = 0; m < MEALS.length; m++) { var items = this.S.days[d].meals[MEALS[m]]; for (i = 0; i < items.length; i++) { cal += items[i].cal; } }\r\n          var net = cal - (this.S.days[d].ex || 0);\r\n          var col = document.createElement('div'); col.className = 'col';\r\n          var bar = document.createElement('i');\r\n          var h = Math.max(2, Math.min(100, (net \/ max) * 100));\r\n          bar.style.height = h + '%';\r\n          if (target && net > target) { bar.className = 'over'; }\r\n          var lab = document.createElement('small'); lab.textContent = DAYS[d].slice(0, 3);\r\n          col.appendChild(bar); col.appendChild(lab);\r\n          chart.appendChild(col);\r\n        }\r\n      }\r\n      var proj = document.getElementById('mp-projection');\r\n      if (proj) {\r\n        if (!this.S.targetCals || !this.S.rate) {\r\n          proj.textContent = 'Set your targets on the first tab and choose a weekly goal to see a projection.';\r\n        } else if (this.S.rate === 0) {\r\n          proj.textContent = 'You chose to maintain your weight, so no loss is projected. The planner will help you stay near your maintenance calories.';\r\n        } else {\r\n          var weeklyDeficit = (this.S.tdee - this.S.targetCals) * 7;\r\n          var weeklyLoss = weeklyDeficit \/ 7700;\r\n          proj.innerHTML = 'At your safe target, you are on track for roughly <b>' + weeklyLoss.toFixed(2) + ' kg per week<\/b>. Over 8 weeks that is about <b>' + (weeklyLoss * 8).toFixed(1) + ' kg<\/b>, if the pattern holds. Weight naturally fluctuates day to day, so weekly averages tell the real story.';\r\n        }\r\n      }\r\n    },\r\n\r\n    \/* ---------- misc ---------- *\/\r\n    loadSample: function () {\r\n      document.getElementById('mp-sex').value = 'female';\r\n      document.getElementById('mp-age').value = '32';\r\n      document.getElementById('mp-weight').value = '72';\r\n      document.getElementById('mp-height').value = '165';\r\n      document.getElementById('mp-activity').value = '1.55';\r\n      document.getElementById('mp-rate').value = '0.5';\r\n      this.calc();\r\n      this.switchTab('setup');\r\n    },\r\n\r\n    clearWeek: function () {\r\n      for (var d = 0; d < 7; d++) { this.S.days[d].meals = { breakfast: [], lunch: [], dinner: [], snacks: [] }; this.S.days[d].ex = 0; }\r\n      this.save(); this.renderWeek(); this.toast('Week cleared.', 'info');\r\n    },\r\n\r\n    resetAll: function () {\r\n      this.S = this.freshState(); this.save();\r\n      this.renderWeek();\r\n      document.getElementById('mp-result').innerHTML = '<p style=\"color:#334155;font-size:0.9rem;margin:0;\">Enter your details and press Calculate My Targets.<\/p>';\r\n      document.getElementById('mp-floor-warn').style.display = 'none';\r\n      document.getElementById('mp-bmi-note').textContent = '';\r\n      document.getElementById('mp-plan-target').textContent = 'not set';\r\n      this.toast('Everything reset.', 'info');\r\n    },\r\n\r\n    openModalReset: function () {},\r\n\r\n    toast: function (msg, type) {\r\n      var t = document.getElementById('pth-mp-toast');\r\n      if (!t) { return; }\r\n      var colors = { success: '#059669', error: '#dc2626', info: '#2563eb' };\r\n      t.style.background = colors[type] || colors.info;\r\n      t.textContent = msg; t.className = 'pth-mp-toast show';\r\n      if (this._tt) { clearTimeout(this._tt); }\r\n      this._tt = setTimeout(function () { t.className = 'pth-mp-toast'; }, 3000);\r\n    }\r\n  };\r\n\r\n  window.pthMealPlannerApp = App;\r\n\r\n  function runInit() {\r\n    if (!window.pthMealPlannerApp.initialized && document.getElementById('pth-mealplanner-wrap')) {\r\n      window.pthMealPlannerApp.initialized = true;\r\n      window.pthMealPlannerApp.init();\r\n    }\r\n  }\r\n  runInit();\r\n  if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', runInit); }\r\n  window.addEventListener('load', function () { setTimeout(runInit, 50); });\r\n  if (!window.pthMealPlannerApp.initialized && typeof MutationObserver !== 'undefined') {\r\n    var obs = new MutationObserver(function () { if (document.getElementById('pth-mealplanner-wrap')) { obs.disconnect(); runInit(); } });\r\n    obs.observe(document.body || document.documentElement, { childList: true, subtree: true });\r\n    setTimeout(function () { obs.disconnect(); }, 10000);\r\n  }\r\n})();\r\n<\/script>\n\n\n\n\n<style>\n.pth-container{max-width:1480px;margin:0 auto;padding:0 20px;box-sizing:border-box}\n.pth-feature-wrap{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:20px;margin-bottom:40px}\n.pth-feat-box{border:1px solid #e2e8f0;background:#fff;padding:24px;border-radius:12px;box-shadow:0 4px 6px -1px rgba(0,0,0,0.02)}\n.pth-feat-title{font-size:1.05rem;font-weight:800;color:#0f172a;margin:0 0 10px;display:flex;align-items:center;gap:10px}\n.pth-feat-text{font-size:.9rem;color:#1e293b;line-height:1.6;margin:0;font-weight:500}\n.pth-steps-wrap{border:1px solid #e2e8f0;background:#fff;padding:40px 30px;border-radius:16px;text-align:center;box-shadow:0 4px 6px -1px rgba(0,0,0,0.02);margin-bottom:40px;width:100%;box-sizing:border-box}\n.pth-steps-title{font-size:1.2rem;font-weight:800;color:#0f172a;text-transform:uppercase;margin:0 0 35px;letter-spacing:1px}\n.pth-steps-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:30px}\n.pth-step-item{display:flex;flex-direction:column;align-items:center}\n.pth-step-num{width:45px;height:45px;background:#059669;color:#fff;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:1.2rem;margin-bottom:15px;box-shadow:0 4px 10px rgba(5,150,105,.3)}\n.pth-step-name{font-weight:800;color:#0f172a;margin:0 0 8px;font-size:1rem}\n.pth-step-desc{font-size:.9rem;color:#1e293b;line-height:1.5;margin:0;font-weight:500}\n<\/style>\n<div class=\"pth-container\">\n  <div class=\"pth-feature-wrap\">\n    <div class=\"pth-feat-box\">\n      <p class=\"pth-feat-title\"><span aria-hidden=\"true\">&#128197;<\/span> Full 7-Day Planner<\/p>\n      <p class=\"pth-feat-text\">Lay out breakfast, lunch, dinner, and snacks for the whole week. Each day shows a running calorie total against your target, so you can balance a heavier day with a lighter one instead of guessing.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n      <p class=\"pth-feat-title\"><span aria-hidden=\"true\">&#128274;<\/span> Safe Calorie &amp; Macro Targets<\/p>\n      <p class=\"pth-feat-text\">Enter your details once and the planner works out a sensible daily calorie and macro target using standard formulas &mdash; and it holds that target above a healthy minimum rather than chasing an extreme number.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n      <p class=\"pth-feat-title\"><span aria-hidden=\"true\">&#128722;<\/span> Filters, Templates &amp; Grocery List<\/p>\n      <p class=\"pth-feat-text\">Filter foods by vegetarian, vegan, halal, gluten-free, or dairy-free, save a day you like as a reusable template, and turn the whole week into one combined shopping list you can print or export.<\/p>\n    <\/div>\n  <\/div>\n  <div class=\"pth-steps-wrap\">\n    <p class=\"pth-steps-title\">How to Use the Meal Plan Studio<\/p>\n    <div class=\"pth-steps-grid\">\n      <div class=\"pth-step-item\">\n        <div class=\"pth-step-num\" aria-hidden=\"true\">1<\/div>\n        <p class=\"pth-step-name\">Set your targets<\/p>\n        <p class=\"pth-step-desc\">Enter age, sex, height, weight, and activity, then press Calculate for a safe daily calorie and macro goal.<\/p>\n      <\/div>\n      <div class=\"pth-step-item\">\n        <div class=\"pth-step-num\" aria-hidden=\"true\">2<\/div>\n        <p class=\"pth-step-name\">Fill your week<\/p>\n        <p class=\"pth-step-desc\">Open the 7-Day Planner, add foods to each meal, and use the dietary filters to match how you eat.<\/p>\n      <\/div>\n      <div class=\"pth-step-item\">\n        <div class=\"pth-step-num\" aria-hidden=\"true\">3<\/div>\n        <p class=\"pth-step-name\">Check the balance<\/p>\n        <p class=\"pth-step-desc\">Watch each day&#8217;s bar against your target and glance at the weekly chart in Insights.<\/p>\n      <\/div>\n      <div class=\"pth-step-item\">\n        <div class=\"pth-step-num\" aria-hidden=\"true\">4<\/div>\n        <p class=\"pth-step-name\">Shop and cook<\/p>\n        <p class=\"pth-step-desc\">Build the combined grocery list, then print it or export a CSV to take with you.<\/p>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Last updated: August 2026<\/p>\n\n\n\n<h2 id=\"\ud83d\udd34-what-this-planner-does-for-you\" class=\"wp-block-heading\">\ud83d\udd34&nbsp;<strong>What this planner does for you<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Planning meals ahead of time is one of the few habits that reliably makes healthy eating easier, because most poor food choices happen when you are hungry and have not decided what to eat. This offline meal planner gives you a place to lay out a full week, see roughly how the calories and protein add up, and shop for it in one trip. It runs entirely in your browser, so your weight, height, and food choices stay on your own device with nothing uploaded and no account to create.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is a planning and learning tool, not a diagnosis or a diet prescription. The numbers are estimates from standard formulas, and bodies vary, so treat the targets as a starting point you can adjust with a doctor or registered dietitian.<\/p>\n\n\n\n<h2 id=\"\ud83d\udfe1-building-a-week-youll-actually-follow\" class=\"wp-block-heading\">\ud83d\udfe1&nbsp;<strong>Building a week you&#8217;ll actually follow<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Start on the Setup tab, enter your details, and pick a gentle weekly goal such as losing half a kilogram per week. The planner returns a daily calorie target and splits it into protein, carbs, and fat using whichever macro style you choose \u2014 balanced, low-carb, keto, or high-protein. Then move to the 7-Day Planner and start adding foods. If you eat a particular way, set the filter to vegetarian, vegan, halal, gluten-free, or dairy-free and the food list narrows to match.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two features save real time across a week. Once you build a day you are happy with, save it as a template and drop it onto other days rather than rebuilding it. When the week is full, the Grocery tab combines every food into a single list with totals, so three lunches using chicken show up as one line, not three. You can export that list as a CSV or use Print to save it as a PDF. For working out the nutrition of a single home recipe rather than a whole day, the&nbsp;<a href=\"https:\/\/schoolict.net\/tools\/free-recipe-nutrition-calculator\/\">recipe nutrition calculator<\/a>&nbsp;is the better tool, and it pairs naturally with this planner.<\/p>\n\n\n\n<h2 id=\"\ud83d\udfe2-the-guardrails-why-it-wont-let-you-crash-diet\" class=\"wp-block-heading\">\ud83d\udfe2&nbsp;<strong>The guardrails: why it won&#8217;t let you crash-diet<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Plenty of calculators online will happily hand you a 900-calorie target if you ask for fast results. This one will not, on purpose. It caps how aggressive your daily deficit can be and never sets a target below a healthy floor \u2014 roughly 1,200 calories for women and 1,500 for men \u2014 because eating under that without medical supervision tends to cost you muscle, energy, and the very consistency that makes weight loss stick. If your inputs would push below that line, the planner quietly raises the target and tells you why.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This offline meal planner leans the same way in its Insights tab. Instead of only counting numbers, it shows a balanced-plate guide \u2014 half vegetables, a quarter protein, a quarter whole grains \u2014 that keeps meals sensible even on a day you do not log every gram. Slow, steady change is easier to keep, and the projection here assumes exactly that pace. For the reasoning behind building a tool like this fully on your device, the&nbsp;<a href=\"https:\/\/schoolict.net\/tools\/secure-offline-web-development-utilities-guide\/\">offline utilities guide<\/a>&nbsp;explains the privacy case, and reputable general guidance on healthy weight lives at&nbsp;<a href=\"https:\/\/nutritionsource.hsph.harvard.edu\/healthy-weight\/\" target=\"_blank\" rel=\"noreferrer noopener\">Harvard&#8217;s Nutrition Source<\/a>. If you want the science behind the numbers, see the companion article on&nbsp;<a href=\"https:\/\/schoolict.net\/tools\/how-calorie-deficit-works\/\">how a calorie deficit works<\/a>.<\/p>\n\n\n\n<style>\n.pth-faq-wrap{max-width:1480px;margin:0 auto;padding:0 20px;box-sizing:border-box}\n.pth-faq-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}\n.pth-faq-card{border:1px solid #e2e8f0;background:#fff;border-radius:12px;padding:18px}\n.pth-faq-q{font-weight:800;color:#0f172a;margin:0 0 8px;font-size:1rem}\n.pth-faq-a{color:#1e293b;font-weight:500;line-height:1.6;margin:0;font-size:.92rem}\n@media(max-width:1024px){.pth-faq-grid{grid-template-columns:repeat(2,1fr)}}\n@media(max-width:640px){.pth-faq-grid{grid-template-columns:1fr}}\n<\/style>\n<div class=\"pth-faq-wrap\">\n  <div class=\"pth-faq-grid\">\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Is it really free, offline, and private?<\/p>\n      <p class=\"pth-faq-a\">Yes. Everything runs in your browser, so your body details and meal choices never leave your device. There is no account, no upload, and after the page loads it keeps working without a connection.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">How does it work out my calorie target?<\/p>\n      <p class=\"pth-faq-a\">It estimates your resting needs with the Mifflin-St Jeor formula, adjusts for your activity level, then subtracts a modest, capped deficit for your chosen weekly goal. The result is a starting estimate, not an exact figure.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Why won&#8217;t it let me set a very low target?<\/p>\n      <p class=\"pth-faq-a\">Because very low intakes are hard to sustain and can be unsafe without supervision. The planner holds your target above a healthy minimum and eases overly aggressive deficits, so the plan stays steady rather than extreme.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Can I use it if I&#8217;m vegetarian, vegan, or halal?<\/p>\n      <p class=\"pth-faq-a\">Yes. The food picker has filters for vegetarian, vegan, halal, gluten-free, and dairy-free, and you can add your own custom foods with their calories for anything not listed.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Does it save my plan?<\/p>\n      <p class=\"pth-faq-a\">Your week is stored in this browser&#8217;s local storage, so it is still there when you come back on the same device. Clearing site data or switching browsers starts a fresh plan.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Can I print it or export it?<\/p>\n      <p class=\"pth-faq-a\">Yes. The Grocery tab builds a combined shopping list, and you can export the full week as a CSV or use Print to save a clean PDF of your plan and list.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Do I have to weigh every food?<\/p>\n      <p class=\"pth-faq-a\">No. The numbers help, but the balanced-plate guide in Insights lets you eat well without tracking every gram. Use the detail when it is useful and the plate method when it is not.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Is this medical advice?<\/p>\n      <p class=\"pth-faq-a\">No. It is a general planning tool for education. Speak to a doctor or registered dietitian before starting a weight-loss plan, particularly if you are pregnant, under 18, or managing a health condition.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Does it work on a phone?<\/p>\n      <p class=\"pth-faq-a\">Yes. The planner stacks into a single column on small screens, so you can build and check your week from a phone as easily as a laptop.<\/p>\n    <\/div>\n  <\/div>\n<\/div>\n \n","protected":false},"excerpt":{"rendered":"<p>Your Complete Offline Fitness &amp; Nutrition Companion \u2014 Free Meal Planning &amp; Weight Loss Calculators, Zero Data Required Looking for a free offline weight loss meal planner? Calculate your daily calorie deficit, check your BMI, and auto-generate custom meal plans 100% securely in your browser. \u00a0 &#128197; Full 7-Day Planner Lay out breakfast, lunch, dinner, &#8230; <a title=\"Free Offline Weight Loss Meal Planner\" class=\"read-more\" href=\"https:\/\/schoolict.net\/tools\/free-offline-weight-loss-meal-planner\/\" aria-label=\"Read more about Free Offline Weight Loss Meal Planner\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":4118,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-4110","page","type-page","status-publish","has-post-thumbnail"],"_links":{"self":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/4110","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/comments?post=4110"}],"version-history":[{"count":2,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/4110\/revisions"}],"predecessor-version":[{"id":6972,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/4110\/revisions\/6972"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media\/4118"}],"wp:attachment":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media?parent=4110"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}