leanna in beta
← All resources

Meal planning prompt

meal-planning-prompt.md
# AI Meal Planning for Your Actual Family Help me create a practical meal-planning artifact for my actual household, covering breakfast, lunch and dinner. Take responsibility for turning our preferences, schedule, groceries and feedback into a usable weekly plan. Reduce the decisions and follow-up questions needed to get meals on the table. Use the AI environment I am already in. First gather my household inputs, then build the most useful interactive artifact, canvas or editable dashboard this environment supports. Do not start a separate app-development project, require an API key just to plan meals, or invent integrations. Use plain language and keep setup manageable. ## 1. Learn about my household Reuse information I have already provided. Begin with four short questions: - Who is eating: each person’s name or nickname, approximate age (in months for babies/toddlers, years for older children; adult age optional), and which breakfasts, lunches and dinners they need? Record every person separately, including siblings of similar ages. For babies, also ask their current feeding stage and established caregiver/clinician instructions. - What must meals accommodate: allergies, intolerances, dietary requirements, strong dislikes and any household rules? - What fits our life: typical cooking time, weekly food budget if we have one, and a few breakfasts, lunches and dinners we already like? - Where do we shop: the actual store name on the sign or app (for example, Fred Meyer or Safeway), ZIP code and preferred branch if we want local shopping support, in-store/pickup/delivery, and an optional separate store for bulk purchases? If I already use a particular shopping website or app, ask which one. Before finalizing the first shopping list, walk me through what is already in my refrigerator, freezer and pantry. Invite one conversational or dictated list, optionally supported by photos when the AI can read them. Include staples, sauces, spices, proteins, produce and partly used packages. Present the interpreted items and amounts for a quick review; ask only about missing amounts that materially affect this plan. Record “owned, amount unknown” when I cannot give a quantity instead of treating it as zero or enough for every meal. Keep this pantry intake easy to revisit in My Pantry. Let me answer conversationally or by dictation. Ask only consequential follow-ups, in small batches. Make every input easy to change later. Do not assume that the person setting this up will always plan, shop or cook. Collect or offer editable defaults for: portions by person and meal; recurring favorites; preferred amount of repetition; adventurous versus familiar meals; spicy/sweet preferences; priorities such as speed, comfort, variety, budget and general balance; requirements such as a vegetable and protein at dinner; pantry/freezer stock; cooking equipment and skill; active preparation versus total cooking time; leftovers; packed lunches and reheating access; school/work meals; days away; and who is available to cook. Treat calorie or nutrient targets as optional user-provided requirements, not something to infer from age, weight or family role. Offer optional common restrictions rather than applying them to everyone. Include the nine major US allergens: milk, egg, fish, crustacean shellfish, tree nuts, peanuts, wheat, soy and sesame, plus a custom allergy field. Separately offer gluten-free, lactose-free, vegetarian, vegan, pescatarian, no pork, no beef, and halal/kosher requirements with a place to specify needed certification. Do not treat lactose-free as milk-allergy-safe, wheat-free as automatically gluten-free, or a recipe description as religious certification. Include texture aversions, separate components for picky eaters, and school or workplace food restrictions when relevant. Record whether each preference applies to one person, one meal or the whole household. Explicitly confirm allergy information; do not interpret an unanswered question as “none.” If infants or medically prescribed diets are involved, ask what foods and requirements the caregiver or clinician has already established. Do not invent feeding stages or treatment diets. ### Use ages to shape the meals Keep a per-person profile with approximate age and when it was confirmed, current feeding stage, meals eaten at home or provided elsewhere, user-estimated portions/appetite, individual restrictions, likes/dislikes, spice preference, texture or chewing needs, and whether components should be separate. Two toddlers may need different preparations; a teen and toddler should not inherit the same portion or spice assumptions. Ask about changing needs when the family reports a new stage. Do not require birth dates or silently advance feeding stages with the calendar. For a baby currently receiving only breast milk or formula, do not assign breakfast/lunch/dinner solid-food recipes or include the baby in family-recipe serving totals. Keep the existing feeding plan separate; add formula or feeding supplies to shopping only when requested, with the exact chosen product. Do not infer milk volumes, alter formula preparation or recommend a switch. For a baby already starting solids, ask which foods and textures are established, how food is offered, any feeding difficulties, and relevant clinician instructions. Age alone does not establish readiness. Use current authoritative pediatric guidance; routine solids introduction is around six months and not before four months. Do not invent an allergen-introduction or medical feeding plan. Keep unresolved readiness or ingredient questions visible before approving that child’s meal. When choosing recipes, prefer a shared base that works for the actual household, with concrete person-specific preparation instructions: reserve an unseasoned or milder portion before adding sauces; soften or mash suitable components; serve components separately when preferred; and identify a genuinely separate option when the family recipe cannot be adapted. A smaller adult serving is not automatically a baby/toddler meal. Check ingredient-specific choking hazards, food shape/texture, bones, honey for children under 12 months, salt and other relevant restrictions against current authoritative sources. Do not label a meal safe solely because it has a child tag. The family must follow established caregiver/clinician guidance for feeding difficulties or medical diets. For school-age children and teens, consider school-provided lunches, packed meals, reheating access, activities and user-reported appetite. Portions are editable recipe-yield estimates, not nutrition targets or fixed age/sex multipliers. A teen may need more than the family’s default portion; ask rather than assume. Keep each person’s meal attendance and any separate baby-food plan explicit. Sum only actual recipe eaters when scaling the shared meal, account for separately prepared foods in the grocery list, avoid double-counting shared ingredients, and keep planned leftovers distinct. Never mark a child’s separate food plan covered merely because adult groceries are listed. ## 2. Check what this ai can actually do Quietly check available artifact, browsing, storage, sharing, grocery, browser-action and scheduled-task capabilities. Explain only the limitations that affect my experience. Distinguish what works inside the artifact from what requires returning to this chat. Use this conversation's AI for meal generation and revisions. Put a live AI edit button inside the artifact only if its runtime genuinely supports AI calls. Otherwise handle revisions in this chat; do not put a Make a Change form in the everyday Settings interface. Do not make a nonfunctional microphone or pretend a rules-based text parser is AI. Support ordinary text input and the device's keyboard dictation; add a dedicated microphone only when supported and tested. Use real saved state when available. Put a brief storage explanation in Settings if changes are limited to this browser/device. Do not call browser-local storage family synchronization. Preserve saved household data across interface updates; keep recovery mechanisms behind the scenes rather than putting Export Backup/Import Backup controls in the everyday interface. If interactive artifacts are unavailable, create the best editable planner file or structured dashboard supported here and explain the practical difference. Continue useful work when one advanced capability is unavailable. ### Use the included starter UI — no download needed This prompt already contains the complete template in the `BEGIN INCLUDED MEAL PLANNER` / `END INCLUDED MEAL PLANNER` section at the end. Do not fetch a ZIP, request a template upload, send me back to a website, or offer to rebuild an approximate layout. The code, styles, icons and required UI fonts are included as text and embedded font data. No network access, package installation, API key or archive extraction is required to create the planner. Optional live recipe research, grocery actions and scheduling still depend on the actual tools available. First collect the household inputs. Then use the included HTML as the source of the artifact: copy it into a local HTML file or an HTML artifact, preserving its DOM, styles, modules and controls. For a code environment, write the supplied source to the working file in manageable sections if necessary; do not echo the entire template back to the user or ask them to move files. No Python, Node, React, npm or CDN is required to run the resulting HTML. A platform adapter is allowed only where the actual artifact runtime requires it, without redesigning the interface. Do not put the video’s iPhone bezel inside the product. Personalize only the two labeled scripts `meal-config` and `meal-recipes`, plus any genuinely needed platform storage adapter. `meal-config` supplies `window.MEAL_CONFIG`: set a fresh private household storage key, household-local Monday `firstWeek`, `profile`, `store`, seven recipe IDs per chosen meal type in `choices`, and confirmed ingredient quantities in `pantry` (null means owned, amount unknown). Set `demo:false`; set `ready:true` only when the household, recipe and ingredient data are usable. Keep a household’s existing storage key and saved state on later revisions. Do not carry over anybody else's names, location, stock, approvals or schedules. `profile` contains available cook `names`, individual `members`, confirmed allergies/restrictions/preferences, `mealKinds`, editable `servings` by meal, breakfast/lunch/dinner cooking limits, budget, planning/shopping owners, timezone and optional other stores. Read the included Household module for the exact member schema and enumerations. Keep age-specific adaptations and separately prepared foods explicit; do not assume age alone determines readiness or portions. `meal-recipes` supplies `window.MEAL_DATA={ITEMS,R,CARD_NAMES}`. Each ingredient ID maps to `{name,unit,category}`. Each recipe ID maps to `{name,cardName,kind,yield,active,total,ing,steps}`, with optional `image:{src,alt,caption}`, `source` and `adaptations`. `ing` maps existing ingredient IDs to quantities for that recipe's positive explicit yield. Use household-reviewed recipes, sensible time limits and suitable replacement candidates. Images are optional when none can be obtained truthfully; preserve the existing graceful fallback and never invent a source photograph. `CARD_NAMES` holds concise display names; keep full names in recipe details. Preserve the exact four tabs, compact three-column responsive schedule, two-line titles, chef-name picker, dedicated recipe view, persistent approval/swap/repeat/cooked controls, pantry accounting, shopping extras, cart result notices and scheduling settings. The next sections describe the fixed behavior. Fonts needed by the English UI are embedded; the existing system fallback handles unsupported characters. Do not treat a missing web tool as a reason to stop the basic planner. Before claiming completion, inspect the result and test actual controls when tools permit. If the host cannot display interactive HTML, give the best supported usable editable result within the current chat and explain that specific limitation. Do not mislabel a redesign as an exact reproduction. If the pasted prompt is truncated and the end marker is absent, say which included section is missing; never claim missing code exists or require a template download. ## 3. Build the household planner Create a clean, mobile-readable dashboard with four tabs: Meal Schedule, Shopping List, My Pantry and Settings. Make all four visibly selectable using bordered button surfaces, simple icons, hover/focus feedback and a clearly filled active tab; do not style inactive tabs like ordinary headings. Keep the tabs usable on narrow screens. The top contains only “Weekly Meal Plan,” the week’s date range and the date picker. Do not add household names, a tagline, “kitchen notes,” a draft subtitle or a large brand header. Use a Monday-start week and the household timezone internally. Preserve earlier and future weeks and their manual edits. Show the meal types the household requested. Support breakfast, lunch and dinner; keep people and portions editable per meal. For an explicitly requested demonstration, show all three meals and the requested example serving count without rewriting the household’s actual preferences. Differentiate meal types using restrained oat, sage and French blue surfaces or accents with visible text labels. Keep the same readable card structure so the distinctions stay quiet. On mobile, keep the three requested meal modules side by side with compact controls. Every meal title has a fixed two-line slot; the number of lines and slot height must never grow with title length. Use concise, recognizable card names while retaining full names in recipe details. Fit font size to the actual available width and two-line height after rendering, resizing and font loading. Keep a readable minimum font size; shorten the display name if needed rather than add lines or shrink indefinitely. Reserve the same two-line space for short titles too. Keep cooking time and servings in separately addressable, consistently aligned rows across breakfast, lunch and dinner. Preserve all three columns at narrow phone widths without hiding the third card or relying on horizontal scrolling. Every meal includes its name, servings, preparation/total time, ingredients, usable instructions or a verified recipe link, and an editable Cook assignment. Display a small chef-hat icon followed by the compact name dropdown on one unbroken row, including on mobile. Omit the visible “Cook:” text to give the name more room, give the name about 1.5 times the basic left inset inside the dropdown, and retain an accessible Cook label, keyboard access and a usable tap target. For an example, distribute assignments between the supplied household members; for real planning, respect actual availability. Tapping a meal or its keyboard-accessible “Recipe” button opens a dedicated meal/recipe screen, not an inline panel above or below the schedule. Open at the top of the recipe view, with its name, an appropriate food image, time and servings, ingredients and instructions first; place meal-editing controls underneath. Prefer a usable, permitted image associated with the verified recipe. Where image generation is supported, an accurate generated serving suggestion is acceptable and must be labeled as such; never present it as a photo from a recipe source. Preserve image provenance internally, provide useful alt text, avoid unrelated food photos and handle missing images gracefully rather than showing a broken placeholder. Show the demonstrated recipe with its image. Provide an obvious “Back to meal schedule” button that restores the selected week and scroll position. Keep cook, approval and repeat controls independent so activating them does not also open the recipe. Allow skipped, away, school/work-provided, takeout and cooked states. Each proposed meal begins with a conspicuous but compact red “Needs approval” tag. Put thumbs-up and thumbs-down controls at the top right, with accessible Accept and Swap (reject) labels. Acceptance updates the badge and card styling without adding a success/status sentence above the schedule. Avoid routine “meal accepted / ingredients reserved” messages. Keep thumbs-up, thumbs-down, Recipe, “Repeat meal” and “Mark cooked” in stable positions. The Repeat meal and Mark cooked labels never turn into Swap or replace one another. Keep unavailable controls visible with gray styling, accessible disabled state and a tap/keyboard explanation of why they are unavailable and what to do next. Before acceptance, explain “Tap thumbs-up to accept this recipe first.” Preserve the full action layout even while loading or after cooking. Rejection starts a same-slot replacement request and immediately releases that meal's ingredient reservations. Keep the rejected meal visible in gray, with a swap icon and “Swapping” indicator while a replacement is genuinely being prepared. Leave Repeat meal and Mark cooked gray and inactive. Once a compatible replacement is ready, persist “Swap ready” with the swap icon and a clear “Refresh for your new recipe” control. An actual phone/page refresh or that control loads the new recipe in the same slot. Keep the Swap state through unrelated actions and while another meal is repeated; do not switch back to “Needs approval” until the new recipe has actually loaded. The replacement then needs its own approval. Preserve date, meal type, servings, cook, notes and hard requirements; avoid previously rejected recipes. Save pending/ready state so reloading cannot lose the request, and revalidate current restrictions before applying the result. A blocked swap must explain the actual issue and a useful next step, rather than loop forever or claim a new meal exists. Never send someone to a different day to choose a replacement. The swap tap target remains thumbs-down. Only show a loading spinner while work is actually pending. Local curated alternatives are valid if they have been checked for this household; do not label them a live AI generation request. If a chat turn is required, prepare the replacement request and explain that step. A phone refresh cannot run an unavailable AI service by itself. On an accepted meal, “Mark cooked” immediately records the action once, updates confirmed pantry use and turns the same button green with a check mark while retaining the words “Mark cooked.” Keep the current scroll position and the checked button visible until the user selects another tab. Do not insert a review panel above the schedule or scroll away from the pressed button. Prevent double deductions; retain Undo and traceable pantry history. Unknown or short quantities remain flagged for correction, without negative stock or fabricated purchases. Changing ingredients, servings, date or meal slot requires renewed acceptance. Cook-only changes and notes do not alter ingredient approval. Keep approval per meal; do not include Accept All or Copy Week buttons. Replace Lock with “Repeat meal,” and implement the meaning: record that meal as a recurring choice and carry it into new future plans without overwriting existing manually edited or approved meals. Repeated proposals still require acceptance. Do not relabel a lock toggle as a repeat feature. Do not include a Backup Meal concept, section or module. Quick recipes may appear as ordinary meal choices. Put household preferences and stores in Settings, including an easy People & Ages editor with Add/Remove Person, age/months, feeding stage, usual meal participation, editable portion estimates and individual preparation needs. Show person-specific adaptations inside the dedicated recipe view, keeping schedule cards compact. Store historical meal participation/preparation with the meal so later household changes do not rewrite cooked-meal records. Household changes should visibly mark affected proposed meals for review; preview consequential recipe quantities, grocery changes and missing separate foods before applying. Preserve the example’s existing serving counts until the user explicitly applies household portions. Keep sharing a household brief with the originating AI easy when this environment cannot regenerate meals within the artifact. Do not include a Make a Change section. Handle conversational changes in the originating AI chat; preserve the current plan and pantry state when revising it. Show consequential meal and grocery changes before Apply, with Undo available. Keep manual groceries, purchased stock and unrelated weeks intact. Use readable black text, DM Sans for controls, selective DM Serif Display headings, white space and subtle accents. Do not include Like, Needs Work, Export Backup/Import Backup, long disclaimer footers or explanatory marketing slogans. Keep research sources and implementation notes in supporting data for the AI. Put essential cooking instructions, temperatures and any unresolved dietary or connection issue only where the user needs them to act. ## 4. Make the plan and list agree Plan from our favorites and trusted recipes first, adding variety according to our preferences. Clearly distinguish an AI-suggested recipe from a recipe actually retrieved from a source. Do not invent recipe URLs or claim a recipe was tested. Provide enough ingredient and cooking detail for another caregiver to use it without a briefing. Scale ingredients from recipe yield and editable serving estimates. Consolidate compatible units and overlapping ingredients across all three daily meals. Keep ingredient contributions traceable to their meals. Do not convert volume to weight without an ingredient-specific basis. Separate recipe quantity from purchase size, round purchases to real available packages when known, and explain extra quantity or uncertain pack sizes. Make My Pantry the record of food actually on hand, with “Digital Pantry” as the page heading. Navigating to My Pantry or Shopping List opens at the top with the selected tab visible. Provide conversational/dictated intake with a review step, Add Item, Edit Amount and Mark Empty. Support refrigerator, freezer and pantry items, including partial packages. Do not show a separate Unit input when adding or editing pantry items: retain existing units or use the matched ingredient’s unit, and make the quantity label clear for weights, volumes and package counts. Show only the numeric quantity in the On hand table column, without appending “item,” “tortilla,” or similar unit text. Preserve the underlying units for correct arithmetic; never silently treat an unknown bag as a known weight. Only for an explicitly requested demo, pre-populate an empty pantry with clearly identified example stock; never invent stock for a real household. Preserve existing saved pantry entries and do not refill a demo item after it is consumed or removed. Keep quantities, units, last-confirmed date and whether the amount is exact, a confirmed minimum, or unknown. Never turn “half a bag” into a weight without the bag size. Consolidate only compatible units; preserve uncertain descriptions for review. In the shopping list, offer “In My Pantry” / “Already Owned” instead of exposing stock-number fields. Offer a quick confirmation of enough for the displayed requirement, an optional exact amount, or “not sure how much.” An enough-for-this-plan confirmation establishes a minimum, not an unlimited supply; prompt for a stock check when that minimum runs out. Unknown ownership must remain visibly unresolved before a cart action and must not falsely erase all purchasing needs. Track physical stock separately from reservations. Accepting a meal reserves the required quantities but does not consume them. Marking it cooked deducts ingredients once, based on the recipe version and actual portions used; support corrections and partial use. Skipping, rejecting, rescheduling or resizing updates reservations without claiming food was consumed. Avoid allocating the same stock to multiple meals or weeks. Show useful pantry values such as on hand, reserved, remaining after accepted meals and restock/check-amount status. Add groceries to physical pantry stock only after a confirmed purchase or receipt, not when added to a cart. Preserve package excess for later meals. Keep purchases, cooking, manual corrections, waste and expiration as traceable changes; make repeated actions idempotent. Undo must reverse the associated local quantity change accurately without implying an external cart was changed. If recorded stock is insufficient when a meal is marked cooked, flag the missing confirmation rather than fabricate a purchase or a negative quantity. Combine manual and recipe demand without erasing either. Plan leftovers deliberately, including dinner-to-lunch reuse and extra servings needed. Distinguish expected leftovers from food actually cooked. Record cooking/storage dates when known and use current authoritative food-safety guidance for storage questions. If a meal is skipped, do not create its leftovers or deduct its ingredients as consumed. Offer a replacement for a lunch that depended on that dinner. Moving, replacing or resizing a meal must update the affected week's list, including both weeks if it crosses a week boundary. Show the difference. Preserve manually added groceries, checked/purchased quantities and household essentials. Never re-add quantities already purchased; show only any additional amount genuinely required. If food has already been bought for a canceled meal, keep it accounted for and suggest using or storing it appropriately. Use Accept/Reject on meal cards as planning approval, with optional notes in the recipe editor or conversational feedback in the originating AI chat. Learn flavor, texture, timing and portion preferences from that explicit feedback rather than treating meal acceptance as a taste rating. Preserve repeated favorites and hard restrictions. For allergies, check recipe ingredients and proposed substitutions against our restrictions. Packaged-food decisions need the exact product's current ingredient/allergen information; show unresolved checks. Do not claim a meal or product is allergy-safe based on an AI guess. Do not silently substitute an allergen or assume an unverified product is suitable. ## 5. Use the household’s actual stores Ask for ZIP code and the household’s preferred store first. If they need suggestions, offer up to five locally relevant individual chains, verified for that location when browsing is available. Keep every store name distinct, including Fred Meyer, Kroger, Safeway and Albertsons; a corporate family is not a shopping destination. Do not show an undated national popularity ranking or claim these choices are already-connected integrations. Provide a searchable store picker with additional individual choices such as Fred Meyer, QFC, Ralphs, Fry's, King Soopers, Dillons, Smith's, Safeway, Albertsons, Vons, Jewel-Osco, ACME, Shaw's, Star Market, Costco, Target, Whole Foods, Sprouts, Meijer, Wegmans and H-E-B, plus “Another store” with free-text entry. Keep each choice distinct. Selecting Fred Meyer must resolve Fred Meyer's shopping route; selecting Safeway must resolve Safeway's route. Do not replace either with its parent company's name or website. Allow multiple stores, but default to one primary store to keep shopping simple. Ask whether a second store is for bulk purchases, particular products or regular shopping. Costco can be either primary or supplemental; do not decide for the household. Do not automatically split a grocery list across stores without my choice. For every selected store, save its consumer-facing name, verified official website or chosen shopping service, specific branch/store ID when available, ZIP code, pickup/delivery preference, and verified connection status. Confirm the exact store and fulfillment method before matching products or adding to a cart. Check local availability, login requirements and any membership requirement using current official information. An account shared across brands does not prove that carts or integration coverage are shared. Reuse a common connection only when its documented or observed capabilities explicitly cover the selected brand and store. Check the user's current AI environment for the exact supported cart action. Keep capability details in Settings rather than displaying a “Shopping List Only” badge in the main list. An available website, app, online checkout or generic search result does not establish cart-writing capability. Verify capabilities for the actual store; do not make the user diagnose technical integrations. If the store, branch or fulfillment method changes, recheck availability, prices, product matches and cart destination before acting. Preserve the approved ingredient list while flagging products that need review. Do not transfer approval or connection status to another brand automatically. Use the strongest genuinely available shopping route: - A connected retailer or grocery service that can add the reviewed products to the correct cart. - A supported shoppable-list handoff, such as an available Instacart connection, where the shopper reviews product matches and adds them to a cart. - Authorized browser assistance when this AI can actually operate the store's site and the user signs in securely. - Otherwise, a copyable/shareable checklist plus verified retailer or product-search links. Do not invent API access, cart-import URLs or a direct integration. Do not request passwords, payment details or secret keys in chat. Keep unsupported cart-writing operations unavailable and clearly explain the supported next step. An active review or copy-handoff button is allowed when it actually performs that supported step; it must not present a cart-success state. An unavailable button must still explain itself when tapped or activated by keyboard: show the specific blocker and concrete next steps. Guard the action so requesting this help cannot perform the unavailable operation; use accessible disabled-state semantics without making the explanation unreachable. Apply this to every unavailable control. At the top of Shopping List, include a button labeled “Add extra items.” It expands an inline module with a freeform text area for typing or device-keyboard dictation, plus Add to list and Close actions. Accept ad hoc food and household needs; allow commas or new lines between items. Interpret explicit quantities and common package units conservatively, preserve item descriptions, and avoid unsupported unit conversions. Add entries to the selected week, include them in copied lists and supported shopping handoffs, preserve them through meal revisions, and offer removal. Keep typed drafts while switching tabs. Do not require separate item, quantity and unit fields for this intake. Do not simulate a dedicated microphone or claim a simple parser is AI. Keep the shopping list very compact, with minimal vertical padding between rows, small category headings, concise quantities, open check-circle purchase controls and an In My Pantry action. Keep tap targets usable even as the spacing shrinks. Remove marketing slogans, Buy At dropdowns, Verified Package Size fields and Preview Stock/Store Update labels. Do not display Pantry, Store & Meal Breakdown disclosures in the shopping list. Retain ingredient-to-meal attribution internally for the AI. Let the AI handle package matching using verified retailer information; do not ask the household to interpret technical package fields. At the top of Shopping List, make “Approve & Add to Cart” an active entry point to the real shopping workflow. If the runtime can invoke an authorized retailer tool or browser agent, its reviewed action must add the exact approved products and quantities to the selected cart and return verified results. If the artifact cannot invoke that workflow itself, clicking opens a usable review-and-copy handoff with clear steps: accept the desired meals, confirm pantry stock, copy the cart request, paste it into the originating AI chat, review exact matches and prices, and authorize additions there. Explain this handoff beside the button and in the opened panel so the button does not imply it has already added groceries. Keep the copy action separately and accurately labeled “Copy cart request.” Never silently replace cart additions with opening a retailer homepage. A static page cannot launch this chat or acquire cart access merely from its button label. For an Instacart handoff, identify that destination and its actual supported action. Keep pending meals, uncertain stock and unreviewed product substitutions visible before acting. After a real cart action, report what was added, quantities and unresolved items; confirm success from the tool or store result. Prevent duplicate additions when the action is repeated and preserve unrelated cart contents. Do not check out or place the order. After each supported shopping run, display a persistent, mobile-readable cart-results notice at the top of Shopping List. If any requested items remain unresolved, say “Cart needs attention” and show counts of fully added items versus items needing attention, without hiding partial failures behind a success message. For each exception, name the item, requested quantity, confirmed-added quantity, remaining quantity and specific reason: out of stock, only partly available, no matching product found, store connection failed, not attempted, or unable to confirm. Unknown results remain unresolved, not successful. Make the notice discoverable from other planner tabs with a count or Review link. Associate results with the exact store/branch, selected week, approved list version, run identifier and time; preserve the receipt on refresh and flag it as stale when the list or destination changes. Keep this evidence separate from meal/pantry Undo. Keep missing items on the shopping list and provide an easy way to copy the unresolved items for follow-up. Show proposed substitutions for review only when supported by actual product information, check dietary requirements and prices, and require explicit approval before adding a substitute. Never silently substitute or mark an unavailable item purchased. Before retrying, inspect the real cart and add only confirmed outstanding quantities; reconcile uncertain results first to prevent duplicates. Cart results never change pantry stock by themselves. When no shopping connection exists, do not manufacture a real result; if a notification preview helps demonstrate the interface, mark it prominently “Demo only — no items added” and keep sample results separate from real records and quantities. Review exact product matches, allergens, substitutions, package sizes and quantities before adding items. Show price estimates only with a source and time checked, or with prices I supplied; distinguish food used from packages purchased and list fees separately when known. Do not invent store prices or promise savings. Changes after approval require a visible revision; do not silently alter an external cart. Undo in the planner must not imply that an external cart or purchase was reversed. ## 6. Support recurring work when the platform can run it After the first personalized week works, proactively ask: “Which day and time should I prepare next week's meals, which day and time should I remind you to approve them, and when do you normally shop or pick up groceries?” Also confirm the household timezone, planning owner and an actually available notification channel. Reuse answers already supplied. Do not impose Thursday/Friday or any other fixed days. Offer editable settings for these choices under Settings → Weekly planning & reminders. Set up two tasks when the user chooses to enable them and the environment supports them: 1. Weekly draft: prepare the next Monday-start plan and its shopping list around current preferences, attendance, pantry, repeats, time and budget. Preserve accepted/cooked meals and manually edited weeks. Complete the update before notifying the user that it is ready. 2. Approval reminder: recheck the same week's actual state. If it still needs approval, remind the planning owner to review meals and pantry, then approve the shopping list so the AI can add the selected products to the chosen cart through its supported workflow. Suppress the reminder when the week is already approved, paused or skipped. If drafting failed, report the failure instead of implying a plan is ready. The scheduler must be able to access current household data and approval state while the artifact is closed. A browser-local artifact cannot provide that by itself. Use the AI platform's supported private durable artifact/data connection when available; otherwise offer a clearly labeled ordinary reminder or on-demand plan update. Page loads, saved preferences and a “Set up with my AI” copy button are not active background tasks. Inspect existing schedules before creating duplicates. Only say a task is active after a real scheduler result, and report its name/ID, timezone and next run; keep its status, pause and edit options discoverable. Respect daylight-saving time, planned absences and a unique week/run identifier. Never overwrite an approved plan silently or send duplicate reminders. Reminders go to the person setting this up through their chosen supported channel. Ask separately before messaging anyone else. Meal approval, approval of exact grocery matches, cart additions and checkout are separate steps; a scheduled draft/reminder does not authorize cart mutations or purchases. A real cart workflow must retain product approval and its item-by-item added/missing receipt. Offer one useful optional follow-up: after the user's actual shopping/pickup time, remind them to reconcile received groceries and substitutions into My Pantry. Ask its preferred day/time if enabled. Never add stock merely because items reached a cart. A daily or evening “Did you mark meals cooked?” reminder is optional for people who want it, not a requirement. For occasional bulk shopping, offer a restock review only when requested. Fold an upcoming-week pantry/leftover check into the weekly planning cycle instead of creating redundant reminders. If scheduling is unavailable, preserve the chosen preferences and explain the specific missing capability; finish the useful planner without pretending automation is active. ## 7. Preserve ownership, privacy and state Keep household preferences/members; recipe versions/yields; dated meal slots/portions/cooks; stock and leftovers; manual and derived shopping entries; store-specific shopping profiles; feedback; approval versions; and job/cart history as distinct records. Keep stable identifiers so moving a meal does not discard its history. Associate each cart action with the actual store, branch, shopping service and approved list version, not merely the corporate owner. Use private household sharing and separate member access when the platform supports it. Verify that another caregiver can open the current plan, see changes and take over a complete job. Prevent silent overwrites from simultaneous edits where shared storage exists. If only exporting or sharing a snapshot is possible, call it a copy, not live collaboration. Never publish personal family data publicly. Collect approximate ages and role names rather than unnecessary personal details. ## 8. Verify and hand over Check pantry intake and unknown amounts; accepted-versus-cooked stock behavior; repeated cooking/purchase actions; pantry updates after rejection, portion changes and moving weeks; three-meal demonstration layouts and per-meal approval states; obvious selectable navigation, one-line cook controls, fixed two-line meal-title slots with adaptive type at phone and desktop widths, and absence of a Backup Meal module; unavailable-action explanations and directions; demo pantry initialization that preserves saved data; and the revised labels and compact shopping controls. Also check the implemented behavior before saying it works: allergy exclusions and uncertain substitutions; all three daily meals with different attendance; a missed dinner and its dependent lunch; portions and package rounding; moving a meal across weeks; preserved manual/purchased groceries; repeat-meal behavior; Apply/Undo; past/future-week persistence; cook assignments; actual sharing or the labeled alternative; repeated cart/job actions; and an already-approved week receiving no reminder. Do not claim an unavailable integration or scheduled run was tested. Also check store routing: Fred Meyer remains Fred Meyer rather than silently becoming Kroger; Safeway remains Safeway rather than Albertsons; changing a branch or pickup/delivery choice triggers product/cart revalidation; and a working connection for one brand does not mark its sister brands connected. Verify that an unsupported store still produces a usable list without a fake “added to cart” result. Test out-of-stock items, partial additions, missing/unconfirmed results, total connection failure, duplicate receipts, refresh persistence, stale results after store/list changes, cross-tab attention indicators, approved-versus-unapproved substitutions and pantry quantities remaining unchanged after cart actions. Verify the dedicated recipe screen opens at the top with its image and returns to the same schedule position; the cook picker really opens and saves the selected name; and an enabled cart entry opens its actual review/handoff without claiming cart success. Verify gray Swapping/Swap ready feedback, stable Repeat meal and Mark cooked labels, persisted pending state, actual refresh loading only the rejected slot, and preservation of concurrent edits. Check Mark cooked stays in place with a green check and never double-deducts stock. Also verify these age-mix cases: adults plus a milk-only baby; a baby already eating established solid foods; two toddlers with different texture needs; a teen plus a toddler; school-provided lunch for one child; and a later change in age/feeding stage. Check no automatic solids from age alone, no baby portion counted as an adult fraction without an appropriate separate preparation plan, appropriate ingredient-specific adaptations, user-chosen portion scaling, separate-food groceries included exactly once, and preservation of cooked history, manual groceries and pantry stock. Deliver the functioning artifact or best supported editable equivalent, populated with our real first week. Give a brief explanation of what works, what needs a connection, and what remains manual. Keep deeper technical details out of the household interface. If the actual artifact has a stable mobile web URL, verify current device-specific steps for adding it to the home screen and reopening it with saved state. If it exists only inside the AI's artifact viewer, explain that access route rather than promise an installable standalone app. Before handing over, verify the same template with a different household: different cook names, meal attendance, portions, time limits, pantry units, timezone and store. Inspect narrow 320/390/430px and desktop layouts when supported, all four tabs and dedicated recipe view. Do not equate source tests with rendered-browser verification. Verify that no personal demo data leaked into the new household and no preferences-only screen says a schedule is active. Begin with the four household questions, reusing any answers already supplied. After essential gaps are resolved, build the planner and its first week from the included template. Do not stop at recipes or another proposal, and do not ask me to download or upload supporting template files. ## Included template Everything below is the actual editable HTML source, not a link or a request to fetch a file. Preserve it and personalize the two labeled data scripts after intake. BEGIN INCLUDED MEAL PLANNER `````html <!doctype html> <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#fffdfa"><title>Weekly Meal Plan</title><style> @font-face{font-family:'DM Sans';src:url('data:font/woff2;base64,d09GMgABAAAAAKlEABUAAAABcZQAAKjKAAQBBgAAAAAAAAAAAAAAAAAAAAAAAAAAGoxjG9BiHItYP0hWQVKQKT9NVkFSgT4GYD9TVEFUgiInSACCEi+BJBEICtBEvmMwg/1kATYCJAOEaAuCNgAEIAWHMgcgDAdbDWZRIxvDET2vbkPA+M2cLrMIRBFsnDHQg9mYFSVl1ans/z8v6ZCxgTUA/quaZpokjU44GJYVLWAdAXOyw/sIy7LZW/ZCJRjwxdceRnDuNfrauO0FRxnchFN8mjjA2nFQORe1MGA0yebmWPVPfikSLky5DpriyZPEBXfEnIm9xJGR+g/kLTnh5NQHwa9mtrzSQOeLSdVscZmgixC3s4Ezcf+FgbDAFuD2/J5QSiSlD/XRB4ecDUO3BqL/h6mcwJ6ipvcClcZVXJy2vd8qMHY9RI1Vp157HqfWvPdBbMm2DGGnm3XCxZQWLntIKNlJUwZeAuo1P/Bz2kNKgWAWioQAEYj7/8mPGHESNIhqxSgzKqPGelRmXtitdu2014m3HRNj4r1b9YfcrH8BggaiggRJoWlaCFKzkc7MSveL+2lZWL/d/m3FZFRK1aBAAFlNDNBQdhV1w3g7pfY/fWtWv5K26TGYwUKErErkkjWVr1MDllU9cVthT4kot5A4DDbIeGt1Vz3/ce4fz4UkTTl5/QSsbdPXEQs1I1n4sZJjImoM3bsPk8xEmZRJVSEaEWFF+ae36edbfstvUavVaskgXJHNAvxIkr9O801wc4YZ+cAH8XcYK8IqXKWoU6UpMymqZPb/z6nfteMmBUqb3xQGCc9+NvsqzhBxAZM4YLOMkoX49CRZlgwYC9eiKkvYHYKhwB6wEO6FVc8/PGPv7kQMsxbMQtGEN5a0RCrJ86fuEMnLZD1d3t/ntP8OXhIY4mADTWcYYDW1jyR3vx6CxYpWH4EfUJwYhff/qem+eJnsYczWh9XJlkytunQpXcju0iNNYF3MAVe4x+fK04GMW1MO/nunff1bsuTQ7tmQMoowoz1LwZwldCFLLNHJyXZ3S1XPj+binZndDdzdB2ZVHltSFWaMq+3LJgUk4atayeoDQw9yWeg/DtYSfuFopyApCGxJH6ic3k82re9VgyTD0P+HiBFSkF2m6pa9cMcxQkTAM7s8ZI9F3YUPeYA/Zn9VoA004DG9JQ55FJF3Gh0ABfDv9+nMmV0pMF97LlMSVnlp77gFWrAtHzCo1dUhLomrdFwBNaACwObBc8C6k61e2Rbp8r+uahiStHAE2QVJ/gV+x0GSMNteacb/f11pO8/PILnyNWAVUAiLhro0XZru60kLT9cyzJr+yjyhmQnNKjS7QeyTpkudyl6FJwhVTir//76pVfpegZQeyJnhB6Q5/KB65hS47pPjgLXhXFEQ+00Pe411kfv/vVeob6rI/38VqKr6IFiGlIACqf5VBVGFAlsNGkBFCGRTbCexpXGmLWXGad2vAtVdANTTBZLqBaUxdOtt1M5EJlpro90gcyabsMMNJ8g23Mj5NNp4w2SDbP3NHrb3hL9Lg79KFsgSE/Fwat3Xt9cSHb3Hg73IBBEJYZGtFRtCENnzW7WwaajZlRDFnOcAITClyqR+fskxpvaHIbabjGmtIQTHBBEjjs71R1z6n5GrvadHqpf2IWkoIpKwrsWyg23+/xlzfU+zcZOWjamtwZlh2TJFBFR+//NAEEgBRtvbuzHS6FkUUAoDBixYcODBR4CULPLyqFSNWgX004CiIpq0oE07BhiMTp0YZvjkCCMx/oZ2CEwwgliy6/sIEoJCaIQo4qhFxR2B70sDPBP7RzzeAogBSgB0G6AvAfoJoD8DGgtoOaCjAO4H8J8A9wBeBvgcQFiArAPkV4D8E1AfANQfhMIBCroAgEuxRfdHHrYH0zcWJIKFI2oYMoxsURXdRBxTUXUhoAEQ7Hsec/AwDQRA79FhCFtqmK++Ct7f9gIMgWSQ0Wsv7FSudxtv223dc/zbQz6P3KO0jV814bLNzjN0Yy12EdIEgCAkQ/YG3mCYDetHdV9ffAUaFLAQHqUnXzXIGZIFUoOIgxkkh0XbcoGlPefsfMj2Ghvg2cJIlpx97ikv2c12bk7RRoIcjVBEqS5zeP9VLuq2102zU3+7Hsr01h5qPR3UaIn4PXpFRrT2kjO7scZuGkKDt1Du/KkUIuO10X+6coyr04//tDKxVHxY1Hcucl/IC7QzORvnmDizcYOxmN0Cfqet/nLGN7fCYTAsUya7Z8w3P5r4KR9+vweU149+Kyg/HR0m+dVvO4jsicPlBLAbOQ7/LdejfxgddIy6jux/xv9b3kkk/GZjSlz/nznOPz8/sdvv3ZY9cXjheEJ3FA0asz8YfEYrAgFgiFLkJk+e0XtNYYG9cE6QPWS3lmnTxTXk+uNbKRTgJsErERu5odfs0jI6vSWNSodsr6Gd2TzTLPdfhbmZv26z89O+ia+X+Z6bDZ8bQR+BBLMJRDkd6ulFejLMDJ95d4kT3nK0Z+tX1bm8V81/Gj5+GW9ZDZlbqO2UpG2f69eLlweI1m+Vm63t20nPwI7/hDt/quvtX+bdeVZxSy7XjZ74oa53KtdvWgeW5MwaGce/fay90YuXnzRz5rNzNeT/ev8LnOD8zfQv7nrpWNlrP33nyksRrf+s+d0rL3Oi01dd/ublV3DCyy9+5SJkGInyyWHxOa7ePdHbF246NL7mlNxrsun84Xiu4p0b6WVIRWyDIj/yZ3oKMwLAEvrMwz+b4ndlf049vBj/xNnGmc6351uWkWUcXObbQuF1VPUubs3X2q14bW6Wa8e7ZrylbF1L4Rk7j+Otlw8p+8seIPTuxf9Sbj/6DHEUd4r/eui5X978Tu6P9z6/+Df0L+9+ubX51Ze/eemV+3///JcuXGR+feGNWOA3F49Ee3aegMjFewp3/m66+V3Mm66R5efn+1/k7BxPzXJCXutEOJOzC9yvyYo3yewuDtgdDvZ93/n//+N77xWO7//hYz/f41d+OEwgADYaDMIfvmF2QdyvX/a2QxwkAPzmqfjV2+52iBSHHkhwL8PO/tsMXPmVPgzw2VBoKe6jMh/NB3JjvsZ/CM0YXkiSNPnSdVKdt/hc/36tQP9mKow0Q7PcvnzBPNJYN22zYYviv5ybpXPacqyC9cznvBXaBTbNfvAZ5Mw9h93zNi4m2hL3v2PflN++PXq67vNLQQF9iz+lZsILTimB9if1PfP6OuRg1hYjMP7N6bQPZ2sRikzJEdD4JgkbHhQCSRbS4+87yKjlouAGWY2aVGo1SLUhhmtw0/imZrf4sCJfvTdAEzWb4++s2XZo3bETXe+50Dd16a4PB+67+jkPfOJTD2mC/PPjxbL879cTAKARAIBWDgC0K8dVnXL8orutT+/t6yP6mBhzBgU48fydzVPj33YmAOCoGgccx+N70x74cDNetbJXo+5MM7BORiMjW9sukSMOK187WYEiu4DFQflGqFFgkwR8B+ByjevTjpy7ZueHZgvA3LZmflD0nBzR+ObtvN/d2VdrDjJt7/haHk/q6OeqsKJ+Bhfu65wUTrlZR0+r3w6BuYEItMxqibHfNzgky5HWMQ+ycgq8DOIVNDKSALHSOLQ5Vqc4i3hF+61e0U6z/nTbRbq7tzGdAsD0xseO3ke71BsBgA+TvQS4c2UEi63hvnqyF2ik21/tVLui0zaKbtMS9NJeYBD3HJPG2ps/dAA7hfJD10oDsQNYVvv7BD4fsF3e2o5tlVu2yhshN8gjt4BZaZ39uS1IBq2A3Umbl6LU7VS0QW6m7eBp9dthTXxPpLQ83Q63BZ0e7EdjGGF0iWPT3h62RQd5ekcF554zwGXED+MiBPhyUjvTwjt1O1rvZqb/bwPXUr2oFMG1xZLr2nI5vqRes36tWVn9Wg1PN6FdEnmVqgsGs782GgCA17N62oReju/YxR3kUlEWWh+Dv0EACMA4reuuPBcRb0eL43jzMMin0XFvR4v5m6SqP6PaNetzaCtfjdrNGzvW2m613e7RS0N7k6Fb0XZUCTZCUQirHfS5v/5K4vRU0lxOygGuRo+jxzVcImtyPWPGIKVtGG4OEACobZoiHwQAzHqOCR9Rb8so2+HdiIM9AQDwdqWb0f9tRDl+itGKVO12svgHN40/8k4bfGvKBPy6MCxj4GqPP1mouGW202Bdt4RciR7V44f3W6u/nig+uZjNyrFd0o7kllJoTkLb0/3Lu1eeaSS9AwCAERqhUQAjpFUJK2v/x+q44ip9+5CvR/Dt7TAMAF2XTWNQBvhmsriBVWu/11vLPvkEW2BMiT5R5FEyLxxJrU9JA/4bi5Xw+/PPfH28ubOG7uTLDUTZAegMB5gf+eXmpBJ5AcVy1ZmOjt68fjmO5sLabsm0XBEMuby/yclTk9lyDGJgm8A5P//CxQBPeVr9dpjAfZFhGdrVyp1CO7Q9DAvLGF483Tzt43F/pOUYEjL0PWsRwbYDAB8JtIlQBGYtxQW8jzylvkZa/j7cLAAk1Qx0bL0EW0lY7Aw6lWS+XAANn/Nh1wADy1nc1ziNeuP543Y/Ob/L2rR6zm4/nU80PUziUW4XTxIIQwmhJnEZNqnwhHw+hUpmlcyvb2LmFlqI08+M6hm1QTl9E45t1mI9rEr3ci2N/KzXm2s1qIP173S2ojnphKw9Ws1FkGRmxasmPx1gZJGEI8l20mmAI67uuOKp3x3QnQqzmIosluHbq/VxrYbwJU3E6Gr90knWJU4nRslhbWy6p+lJ73KJb9DGUs03jNKCN4illa7V3jz2U4U+1KMuIWIlzam614dygvkC1ddIy42GmSjYjvriHnLmyuhO+cX2KEY65aAqN4d+yoDVntm/H5Nhdb9szfK1RLKss5xNIeCYlI28+qzs0HuEHbrpcRFQus2U6bkTV/Z7HqlGI5MTfq2F6KwQMVqRqv/WaIjvSaQKg1X/zmPH7sfcfBnlndRvB4gRvQhZBi5aXAbWE1Vtl7X34NCqbxYH7MBPbCFneD5MzSuouToIrCQG0jwLlefRzNcBeQHisLBM89fiC5IDHsOxySdsSvDU9pxGPc99yqC8CSISWgMZ3FhCmT5js2+244q542aWa13K5J3L3dzWMmCxpBnw2Q6VQ01bIRJ5pHZDO6zKHEt+roA2e72lvao0yVzWmUQ7r86P1UmaPU7biGea5xHVBVi/z0undNjqpw+ZSNK06QU5YBdXniaLofTTlR7f3+lmikHK0msQDszKYXm9C2NtBVRpDFq6uLPXKIb7wryy3L9uYytNV8SNHAwvLMGaS0aE9K5kdDmSiQxVHCTohBVKNr+m8XaaxkKp3x0ghtKE7YkshQgWgZFnscQDgqjMCnx7ta1rt3pH4lgN5FQowUUBbS22ZR3B0GlSiq5lEksEz/w2DadC10WExOtNoTJW3KbB3eK4p3U/HsQRz/sTL/CF08+u3S7nNKkm19AC46ebLlDIBsL7iYiBZI1qIu/g8QdtUReeERaEY9VMw1N72v43CPFCpDH8f3RXH+42csG4hP9CNYUEV+UEZdEm834Mn33TsDbm4X2nTVCdaatm6/wCYmZ1Hj1l0G6A3MICAED6TmQ4igSA2z1OL4ZxLVOicZujNxyEmLHmdmi9I+ay69+4E6polBpxwoQWIZBEUWRQkahWDAIARP59z3De7tvJs5Z2tBjFcIJKAArP9M4/fs1Ne3nUIKlxKkM77Qevq4yoP9BUvn+tgUDLdHejAgPd4FP0ThsGtj4ew/3FcW7W8U/Naqis6NCALszmK8fAweagERf1vHWwctyMp3z4yMWxidsjDaLTL/h9XJvGvikqLH27F2d/2RtUz75L1E53r0IUNabmBstmh7zUfGD+YuZFZ69C70mBb/3WvPo68NP51KX8dL51ACLbk851bRFjxPEewsIoDTegRnCjS02NKhucjmIMaizjQOOZEKFJJoGmkSo8nVPbEX5lroRKmeOvoHrW9uvImc3veNRpLQRA1quECvtCZ8DIxTKwAqymbuq5OwUg3TaPhR0VuGjMFqLE8QpPHH7UmFuIzVjFZtph2ycBwO5Ne789rfvx4K3GE9Nvj5KloHqfhq0GAzcNNi8ZfXcXkbcCjCJV20aX+IdEvmiFHW0RtN2O9mVPqa+Rlkf1jJXVsW9j2izd/+LSHRwsuwNr65uznqnxgzSBXcM9rfvxQK4a3/s54Wk2nqmbn5+yMRdXEYSdO/emZtJ2qjt3NxGx/RyjSFUaJGpzaOKVaQUCMGwy4HJq/najzMi0cbqTGb3usDrZq0yGvrNZURGoacXbfY6PCy9TBwpuA69L6V2I1vSnGKVI1Zs7cf0UVz31NdJyebdIrxaqtqwO9Jq2Ypcn9hUGNaeuMndkEd4qalqRqnSFQA/U10jK5R1IwJpS9lLDsotWQ7BpeqDujk1D47oc2yx4J6ze7akDdXu7oSenZ3QoU8C6vIyWvAyuHoMAwEN11cJOXc6ioygAAeCP2lVOnYfd9faFDqx/cpQxT6odonyMqypJtwp/ZDej5sdNniyuoKiijxibNZ222IYg2FSyJ/pCY3g3zJGOAsdmbhUxa2EkKNFkMlbzzlPVC/2En0bUNyuP4CZDVHLVlCJVYcTunCiTT7HSsNtpL1bhS5aUrozb3eAbA+vw99YTCAA08BNLEU9fGM/0zc/X+F0XyzcVOZcwbUIeBQNHtjqjKWWTqdRIhI0WrmcXySS9f1GcQoPLywIEwvIaEgDEMmE9hppkEqtsl02NfRndJqP38ypiNcSSR+kVXT+8W6/3aMrvjq3xW/Br2p18/8eBz92GASA93rYKpOw/udB1X1PsyHW8yf8dxhVP5vmQT2d+9lJ3E0bviSnfD3sH3KvO3qLr4fVgrF7M7O5fPRZUddxb0lsqbKm+O05KLx96cqqhnPm7mlW+2qPBIiSa+vT4tOSD33myRMUR/LCSjxXSePh5Dqrzrh8u3uHeb2jH6w/9KfXRh3Ti+OOeMdp1RE88+DvLM4uSmkUYlHAi1rKLSvtI3yzuTEJv0WIbNC+Fofgl9/VQK2aMx/6sMR+jkI9rtFDb1xBPQj6eoxPtw+ofrflIjxaPQwBIZq2oR3SNtHZwhU8F/vhSuJXh4ej7/jinJZ5OehKTsaHB8Oj8WpF5cPYzx1C/KM5qm+XjjE70nbtTnoxn8qVDEdcgvwKEOT1O6acYoucirTjQAWutl/KZzyT01X6G2Nb7DHHZv3/W4yHvi/Tr0+L9fjabY+2md7/usWJ7WGaaPh5Dm/pGXzeJMR+K0Hph/dejP/VEFije3x+64WKx5u30XCKPM+DQsv7lxR4dLXOk8p7WW3eYI1nYMeh8X+C4E5PTqy8ltv7EzCngyyjHvn/ER0ywf0Yrl+zH5uRBldoQ03g8Kc/4YO11jN7+OinPYqL2B1OHOXEWNWTFqDhDTGaaaCtYSR7rcv+a1Taa2wEfs9PHcbQP7cAgOtqP5UvGLd6+rvENl0mn/j4vJ9vIXxRnPOOzWeYfMNlsULYfrLJmHpfZ4C3rRLKf48SJjb2pE97XG2l0AB8BR47/H7TDp3LzCXAPZuEpY4rLp0K1eQKR9IjXoUZnTElEWwqShsdvz8jKKTzSVUqoqtmjXH9aw9DYrj37Tp05d+F95sA8rEflGxAJHIMknqmsYKGI0wCbDoEBlQlSAj4LIhucFDQOaGkQXEg8yHxgQhhmwMgiLUe5dDKbEzMITFUgNSQsQbEC3iosQzjGbceufTo5epu4gTd2KGLAaEEwwTsI4oyEfmTxLgFTfAmiQBIgpChI35M0UHXvvNFVKVZTwBQ1FS00CCsrRAtFB4LRn0xUtSZi2mDQoHtPP4ZGgBWkNVVTmYgx3V9Zh6CXMM5ygLs8xGwcaNOzTUqFRaXJIMGONFsEXIAign4QPBBtbAQMLRIJsFoMoqkUPaiRQdzaB7pgoA5to2gSEFpQy1ARtKAxAEWKE5OBamw4Sj2RDcDZFLSChKCBJs0CEeqrgoCtaQo/bKha6DJUWFN9gveT8+M+uRBJvnvJIczbnmsJ8Ourapfgo1cdR35993eObfdt5n3ffGKz9J+pXXjmMBVPSOz1wtMiaVpyb6La2O7RbxQgutv60cvWXhC9/lFbJyj0p6Nv2jvawPwXC88Ax5emhmrwfemoaYWKU/hj3NfWDc0JaKFA2Q7UoIaaSsq/V8FG1s0+735bXXVHk3hU5okmaD7FSMk/yn7MqNegzjWjANGRQOH76Sgt0g2MEsD4O0Lgbu+Vtjbfmb0CMtpKnTP/2ha2HdN3s7fRBT065JVnWds3vdkHIIHRtiy78SdrJPA5YfhBan323JInjn/eThO4adz6rDWtynP2Ju1iBKwGdhgJgxAFV3w/fU64CHH9vm3OYXtUQPYM5lAcp1k2uYXNbe98rs/jvI/rmCHfXzLtOh9gyCmoX8ZPndUwOJhhnt7B2tQ9utnEYDZSVOka5AZV3li1gWpg03/CfIXbT7bYb2aQfh5T8Xjhx54pk+bqUCv3MHdb2MZJ0sfNy8YpUseZ6KzTpPg+1pvHM05QlcfTLajC4xkrYP3xjBKw/HhGC1g9ywQBi0en8djG83h+4z0cXt9YL5vHNzaGzcjZyszUaYZW60+j0mgXXEjnaDY08OilPfKPkkJUPkbUcSyVlRIY1fqDkS8Wgn4VhCB/XhJyj8mvAeQCVAQegdNMX8Iv49/QZYL3O/FdqHGoz9vevwHoHt6FX/xXQ3yTDyZ2kq/Fn3Sjf/h1G8OF58mJr3hzzfvP/P8/r2+FL70cq1970SH/fUMA8K/FKyRkIIRdYeifnvvZwJ4XFrbj2U36Xvji/JreJ+DM9RVt50PA2xo+4MOferfyATLjvVXIsJFvfenPb//hL6Xr0D1p/3HlxoubdPx0W3R/MR7+OJOmLLp/mgx/jEhdl0CBQ+7T6ur9a+33omzrd9/851u/+g2fZh/WshOznO/n9l8WI+9En9M5+ybzTsf/nvz5myf67YNdsPpPRBCF4PuP7vxi6j5q345bV7fHv97s2ctth+Od1MtOsu3dI1H+K/fRZK9Wl4Z/lvtwopc2nbb/Un5nJisLJ/Cjb534T4Yj/+j7z49D6mK1sFfuvfWvn6NIqbzNg8PZ2Ou6Rr/swqNb7xXfx7Rxdf/wn9/+/V9K9yGsTh6pzre/qWO1q1qv/vhe5Z2cHe6dVF13PvEs5457hZnDyn+u7X62tp8UDuv7/xsLZ1oypfDuO+Xxt/6Wj++eurczbu2ZV2MTbLKGksn5L36Suo67t4bem7Of/jwf0+7bTPy/nQ5/AnndrTX90/zAM5BRvHW+DP74PfI1Du/a9mT7T+FvPtOfgH5VU+TX/Pzezx1mLjRfj/7+OelCrrYmxpsH3/hftbaLVbsr7p2vazqb02Vf/+N465t7qXg2YH043fXmbgE5ak9Nh9MfvlNo6udzzyObv13+8njbW1qp3emm4t9M3/sC6nW/MUT82/ey053XM/1hX/69GVYaztq/TF1/zgnbus1Jm24l/fOi9NRe+v8R+kqvp64/78TY9VvX7k7snbWLuts23R/aR3x6/M23xMXpna3jf75D7W7V9r3kP/gWk/czk9Hl26/98W+MhfDtnJHl/ncVtcqb93J2zXe+qUNrqSfDf03evk3jEvPbOgdnm9/YlbXWW8Nrw9/+mFTGcteB5p/ezrIaVviDoYv/PBn+OImc6tv29oe//Z8IKnYeX0/++FVL7OXb2Lx9sueVvZCC3nN/eLrrjV2jXwzYpe+89Tm54l5C1zN933Pwls7Fd1NdUL6v8vLOJXedNhevUYef3MpszpbOvDv99Sua5maxWhjF6oPLLXuNVpezdn+jmVotl0vzkdkHr3KsftXK6qPzD17RMX9vlJ0t9aXndvW2a9vsDcM/fU2/92phvIq4Mf7Ld8hslmt9uxvLv39VOcE3j8knntdiG7dZ3A1/s+Y5f/4+jdATT3T0pqvdKV7/86frvv7oCTNjvfvzP8ql92K78G6iFZ/jV68968hZnV7CCur82Ydrut4PBVAzkpBcHTFumx488p03fvTOqc1yTfDsY1rvzWsXujvb41E/YzHYvv7kzrI3DYQUgrLxx+1Zfbb37fdWaxIAAgAP8PuDaxvN5oTcHHYkgjU9/hecvgkuz2vOJN+aH3j2rfDZ/0zQ7zkqbMeaTkQ+wJHj71pIr8KzbvQ1DQA9vYHX33JSLiSIfwsf/mttJ30XfBaYiAHsg6Vfw+EA/+l/rLmwFL1Nd3F5WXMX57jxgtrvYfXtIrXE2Uol1HTnOuPQlD6qeP275luyWu4WV7nCla5zlFPc60z3udWSvU50TbFgtdZNJQyog85z8aWatVV2sqNdVJFxl9xsjwda7SNK39MxTnKaO93tUne5h2ilh5J4hXJvOcHkvkKLy4Qa6wTHD4m4oUjNcJbH1C/d4HZ3uM3j6qe/JKMtCqp7YmWpY+cHP3NNMlIqJrKkmjowI4gABsDW02B231EwSqhxCmb2CKP6v8R8FQDQ3z809PYWLHfxr4bpLX2ScXGp9NhjQQAebqZ5FMBj8+sAn1op6Ls4kUWJuFtch/T2fOyhDVPGzvbF2vXyfvEGCQo7Dk7w/ot4/oEXFqgrlFy0Df/uR89wSCTwt/eNvSHgXxD4+yAkE4x+Ooz0UQ4VmvWbNtFCa+101KeuaUAAgNKwAGdjiMTGDFIbs8hvzKHhljyaMG208eONDk53udSGRZjJo8m8IIK+N0FvoI0+f70+YG9MCsLGlJj1KsT3UWL2qgCYdgfbVCrDcB/7SydQ+1Ndpk2ZePHeJ7D27jXG81n3nMh8pXK0ADdefHv0K2fp1YDdWoRZSNkToFWXxPU/UloUSNjob4QuZn5VmrQaNu5Fd58J/aSf9kN/PvjOev9W1zcCCgYY6TYWAdWatRnhw+mbnY7alTtA3+bJAX3xeXx2QN/0+f3v2ZqnrZQ9Yj/UkuIi4hb3o8hnfdaX80WM+YfnVwR0qjJg2l90adCo0iCvGh7VfEqNGTGqTK8WQa2m/NR+v9Cv2aT/GRcCIMBghCy4Okcgis9EI2ia+sc0gCmYK6Y/yCIbQILR4o6DT3e4XaC2tR6evDaB31jj8+MnHgDe35HsgiisTUW0ItOXAL3AXMBD0Eni/MkMEHpuJ54ujrFGRj8fQaxuob4sZkUpDEUv4zSzpN1FfZ3IgX4CDOI8EUcgAjFXH0kKFOUaAcg7PdKdMpvHm7m9AsIVZ3o9AVLHmtwR82QarmhoRdU6ONe+UbJB2o0I3IRAjJLdVQq3BptpZkIi2u7npwa+gebF8I5ACM/JGOm5hpTNOYQMLSgVI8Fj5J75GcIgPrzYEFM5bkwYIzdWtvGpjnTnI0GKJPcLw1oNRp45fnyt/MhORkCGD2a8usEtpSErzfBgLaeOZ9K5cxR8V4Svj8WYxLj0cCUE4xBj//0APO/zjrw9s6P9+tA/JPuihUHuz78Pnf+jNNsttR2dv21gtTObCxXBo/4Y1twExIYSmkWdE1Ukt3tY7RU2Sjw/ycfiY0RVOlZmQ1DT+chiz7zg0vOztDL3kqtVoxgalrJV+/lB1SVOEUnMxYNOjszx63tkgqMEI1mMecbRuKdnfkSULvDbbLqM0kAgw9w+cUReMRTr3OdDsuryh4JwnqvMuhj9bjgD1DMf8L/5n4iLhQ1JWoc1lE/ro616sUDsBLq5F2PoWbGZWSlDD2m50E6xlO8pQrveoQJPJmxozJDLwbMg+y5H50uWPEoDE3bQdWX3cc/+fP5Z4mP/8Bioe36A33j/rmQrMdTzfB9HOdpnX3fZ+dU6zKaz8nSNKJH5LiGwGA6CMGFzYq3g+T0Xt4Xiy+xzONBuIoBGCD95+5i0RXxmKqJLe5WyzfP5e+N0loOzR69M7Tih/Zvpuf0lmL1Dsitjo5zXg2RE8XNpJHUWO9cpX0+eURYf5kNI3LblyZKr31c1Tkdv7pnklQOGTg4nWQwzy4V3tvGVinDRMn3DDjpWsjPSVtPt565bO0uPTj0rQ6WCdfSL3WgoT+6VCg6IyljEroJJdrhZ3RzvKBE31DPuncWbKNPOCt7V/71VyeZd7WFVtsj/xM4SdJJr6rsTRgCBRO2lDqj6aja0W1G9TuB6Kma8suIO11iUZhwH/Z3g2bpUsD6G0CgaMhF5HVeBb3cmEi2qV/wzPMJNm3sC1jyrfUz6p/WfW2VMpFd7PbhTa6ONINMWi1BvxKFDb/GM5XPFcsKjUCChAaNZeMaDp9Gkp1AkNZTYx/4YFnUjmtZ4Xu15Gl17AL8zz4/meCWXj3pP+L2oe/RvTx9sJDS7PuAckaZzeq8xdP2oGJGzo0JZ9CTGUi2yapVyeFyHZFKWE/JyLyPj4+WHzILSXFc6xRUeZZGk2FqFIsjLrfycZseSBppK7b4AC+tIGXkwu7JDOVJt5StH57ShjKL1ml9rTcw3FcDS88gzUNk+LNpyxb9VCUwxQCafnqkmqpDx6ZUbpwPBNgbf5DxsuGRdvXemF5Wk7fVcoJnJbhNr/bKBdGIgOTaxX4LZ5wT9kMSEEnLCv6VTcDB+ZoGVYiEkI8uOEpnfz086eOkTrqNjCaAlN9IofUrz4GLUDNOJJ57z7MH4c1ArE5tA7NjaT1+Gx6wJ7oMgORDIOPgaSwOstFavrSLUa/hRA1t5/ft/GrkSBS77IEC5Aqkn7mLBHOXmrhaeXbhgTX28rxML4w6CqTuKNVjXophVpUz+0fPmJrrTzjtVpoq3zXFXfh3t7D3p8Q+VTc6/9+mgu5zBe+NHPmqjlW1m0LG/lA/3xXSP6WzDKd/NkMc/8zLsk4fyRzrOgf72FPs2d8yroQa8kUrT0Bl8lwsd1rV7t9nnVW0NHNtB8Vw9hcuwuxFPdSlhM5hHe/eqT8nLvfpNq4dUu8P1KtwuXgukaJXPPokx4ySMK6snvMs9mZ+yVvWtUQy3j0QSD4XO0YQL51V0yoWRrmEaWgX1DmtE52o2I6mpihGNdgzJaps1VHVhkVaLVd8ZvF2wMDuiJL7YUDKcuiN+tat3G5r6dV/jIPrrB9RgQ5+Yp+km6wPsgXZ1XmxNYPyaCARdWxKszL7a7TV20rsrmkd37SP5WutEyHLc6rs3j1BrNfgsi6qpuuPI0FidN5w8e0gC3XuB8LVXMxjpbfggsC2BfbOT0BpYzS4qNFp2TV5sQyBf35tLlMkNUbvC93xgd5TzdrVSCiv+9xzwgSP72XRE3lxxsKzeMFYxmHaOgIEZCx/45IyJTrOw+FxPLcd4UnsUs2pkUrsOO4wsojqlkO8Csf3Qg6uZ91uXWluNGtupGXigK6UhkKCqsjr1zah40u+Hiiq9s8ocH0ipPblssKw+N3zIZV5sbzU/fMg5XKmtqBvQcEc9Pu7YAFJXUTR4Wlv+lxJV3izXzv7QOKX13MhH8+UwiAs+4C852trieGRfoLH/YGdqbfX1BoPT0o3KJ0pL5ZPtiL6za2Zj5xsaQr8lohu2qUTeLIhZda0mt7l4tXb+z9LGC4Z1nNLPZJUq3PmNhOKieF1+mCusor5+zxLyfFlzWNrGY/XXaiyORI87pJOIP/jzT/DjTzU/tYXw2WvKzy5AHv1ALxs4rmUA9OKq7vOXLLW25PJ9gaqgbFtqaVW31dUGfTMqmfT7ITcjesyQTipU/ZvVYFn9wtUi5FbIpNzKZqF7d1r5Lp/OrNTojL5dQK9Y76he78A7wvyfFV86GgTsgETEKW8SOnfk5ewXVRwSTQa32Y0WuUKvd24FGs7SJZTXaHzUQlRPETA1OYVEUui8Cf+Kd5PVhYh57iALRYJsoUfsy/7oAsN+KqSA+HqrIf0ywcqimaQcnq9BCL/97R3tDe07A6DF6XvF2v7y064VlOa2kXl8C7nYja64Tpdr+/vEkBOxErgahOvB0ogVuPrqin/BC6e8FculsK/Uf36lalkDpzRA/qDqA3/lo2nwSFrFB+Ufllc+hoclPPxjf/V05RPV8ER1xZdPRwNT08SqM1zSNLM6DLCOk9loZGMxj6evLFTNcNOREjr99ojHKdb5CjhYgFPsV1hN4aXw4NcP25J1eWPTlfnRo8zjBzZlD38HZhwLzfrxa4goMV9xpY6XMyX6Fq102G6TjjVjJXqmnlXkVyiK/HoWk2NgkVVVNQYCFafvQJUjDqdy2Pxtr/cPVibYPUetAj5DT6eWIpx089LJ/IITe6o88dbhasjF6Vow6ajNLh1u0WEKH3sx1XXFHOGE73FoJlEfqs1hikGdMNUBwjejiGzE6ZR/y7QimLkO4Xah0Smxe9W6ELPLrizSUYptsixNsU9XXCS6+Z1zRaL1kaFRx7r9E8OiV5LiSMi/LmIcDFK+w5qJEoyPL/zikd834zfltWhTGXRVWlF2UYUl7HxJrIZ8NT1YYcW3+P5zXF70AxcLdxcWalhFDFMty75T5WPRXXw+w+ljK9VeNt0p6jLYy1Khok+l0k9EfI24/12oksxdy9BdVgJ2xZUOi6VcntxFowUwo1OtR/QSKkKimARUsMW54qhdeMDj4/AZOXh8AmB1M8B/pieT7hZWuKqyvScCsTJR3MUAjP6yae33BojHJcVHrriuADa/RnHmMAz6S2h00oVPP4dDPhZb4qDR/KjVqcXkWn7Bv4gGPt3l9PDy/5XPD7+SDmj1sMUSdzGzVGtz6XSoVkJWF5AwHhsoOEMHIh9xuhTDnYjB2A7FsMupGOlogTEMtCI/glJL9TQG3WSLS1FkqaIt0H22bo4ogX8sFE/HmjHpiN3WfhC16DB5gD3KXM6oOAHvfMacLKdLsBZtYtZml44++uDRsdBNNz+EjSXm9ZsZVYFl/U+bDZWQfNiRzvPxcibOH2TmQTjuY2VFTLPGI3lrpJj412Z83oXg7PNI6ld9hcV0tatA4g5aIjEnup/F5eem1w5+s1yS82QEj4V5CiGZrt5E1IiZZVhZ9LRh5iHsISbTzpLrIQwnnM/2oy7lr9GVcsWZw51g8bBh7VJs6eFVOLLquRgLzx2IdXUvwdBS5fBR6Drqnuc8E89DE5hxfOPxDWVTV2D6SmBQZ6OB6Q9gUlY89Vl/cvaFDwJLD8LJB72yH6BQmzedrZsKtWQdNFwqwUpE6r9Ijr6wMJYkyI1zXXrz0t9dGQl4fE4GXheq9TjvqB9Zam1t1Tjm9Y74NZMqqvwvGI9B6EY5k/4AZ8KNCEVehD0R8LMnvSg8V7GHrnnUIF4FtdQgnOmq6xS3cAqfOM5h/T9XQykBVBys46pV1QxRmZZNqdaWudylpvEKHVBwSCtLWR2id28s0GjJbLa6MIfMXLdmMXkYD7OrJMw7KiWGoG6KyBtkaXQVTKaNXZCprty/VUUvxFhiXlWDBHYt1KidXwevNhobF3yfR08AcHG6erbEVe94xK7WsFFaoZXNLjAiRQyhudjm5A0MGoT5n+50EIXcknyJKsfN4RB8MsRLAhRnqhfKKjYNKHsVTwwE3ZoKttDhsD5oFkv5DjVzNrQ+NJwf7rMRWNJaobwWlYpaGhCLgYaSKToGk6JFSTQ6RqHq6AyqDiPBIvRM6j9U3hkFOk7TzJH7ZcUEw0IzInWyeTxnNRtBq9hcJ48tcSLNCwRDsUzub+aoDSaTXoY6tCi0kBn1JpcMU/OkOqlcbmwpT7+7QMWpK9gCZ7X1mEks4dsUrPGwqg04/XCflcCW1KVqpBHBclLbo3iyP2i10/WXwUyqVk+iF2vJFC2T2f6prSVBxsKKe+8KVFTmaBKLSqm0KiBWsjoCl6qJ2XfSM69LEbpaqSC1/jeHGvOlXfq8AI745agAQQoyXk/MoeE3tWYzqPRs0yZ8VnZWpFZZHLOfKjCIabnkj0Jfa8yjYCI4U6l5jPPId8avGQKG5vhAyyJAQ3gGZFg7c61otQbRw+KrXs9VqPow6usAHpb/2Z4WT38cvjO3yBz9SjG7KKAZ/4wUo7mDFLtMXtrIVqnr2dKA0qQItFH0ISUF14I4DVmVjY8vOn/FBz3/PqHjRh7xRgeBGAkx74rBew24fSMe2nbZdmn0uCd2Hxq/b8zzz3b5DlnmqDtuHzyy/axt29n6gOyLP3HBNG0K/GycNUJWRfyzAWeWgeaKZbkTdElYDIbwxy26bT/Y4J/JzsCc4OmmJgkM7HF4It/0P8A5UV5uzh14YCUSRuY9apzwZqQn6N/DeYX9c6DZizqNJuLyKVIWPfcb9JscRhb5xDLR7DSgEn0x9QVG9rEDFB7X/FjoY12WTz5w7HTiC9RiPbTtHQfkPHiKzOeZF0MXZZxHOfVgNgRIUKfBTFw+Qc7Sy/oG/SaXnkU6tUw0OY1gXjh3Xtsr7NXBvYVjuiFQex5ePuO/KRPdvFkK30x4E76suJKfV1bVfxJj1hy4tXdwkoteA54gZyCM4UzTwpoa4bTJ9FKVTAuP4m5L+AuPeDQ/R9na3VWK57w99hYv72mLVbjp67sbxi75JktEzp1mX94N8gwxYv3/i4/BiYt1i27v4fY6xRP9s6cMbc1L7vK9tRJ8G9dr5LIlAkNTYs5oo0Gus5c5+LlBqcVB50lRZYc5fUrHzm3sLcy5mm2Id1NLzWPbEH9gThXs5i54grots9pAsTNB9f256sRNQNHQaSVagfJvUuF9/mQdSchSA1o/8DnFwmTQDO4iudRBoelzXdlP0vgDpDymYPfBh3FoLU9QpeaQHGbF/fuW11VpcrOgiKhWF9JETtaCsUi347tShnuOZTnM0wwowVzDc/EgxNOubccbh5Lt/4g9Z34u6gXsdQzMSdguDHY2JW7eMj6R16i9L2Hz1onxPKwGjPX6Pfq5/2J7MAiq/v7m729b//wW/nwSzqlq9I06R38HVY/8ceaPV9v+PgN/B/+IqlwF50dK9Kp9uG3P/lNWX71yqLuRdaHMlZb1oVlTqZgoyL7QgqZmf0PX2JQTQGX+sNOyvtO9woVGPraw4grqxdExSpGJyysyxgZ0ho5CNfK4VNP0W6HK6N9NzB/rz0jfsjufuLt/q5oL6TUIUGHQgMa2GO9PT1+Y828BsrOtgd5ceXcBUe1ACvOhXj9w6OCyacNdazoCZLoNyU/WNpTSGvyevASoDx44cMS08a6alm9kgdzgup1CHOrvzu1ua6BDbHCU+oquStU9D9TiOyzV5icjECuzqlQ0e+lUSsbjjT41qNOofyop/aT0mQLyb98Rz/x073c1v9FrAdUezxGDgmvNAW0vYKGjw9eef5Yt+rXh+A7eOWN9aD794emXnlcDEVTPrbx80cq4+PxznOefvTYxFolETowKuKTP3DrGEfheb/rwp5WXn1MBEdTPc/4EzcLph5FfyLgVej3758fOCIP09YnRf7WK7LXnn+XY7YWboWB8B58kBzr/aWTyxBFOxEGTM2jogoM1M8LgAntporvLf0P07mGadQiw7SnXUtDK4bTodBcTgScmTiKEFuECMPCdhMGs8Y0+jVnnAIk1UzNOQB31UnOadI69XwDXF6/94tovkfrlSrPyoF/UL/HLEfAL8PJGPvNBe0Zy+GjeW76cKfwl1DjsXXbHj/irpmHlaAONHmeOa3xtH1yJl0BnOpHm1rp//CazvUMJcAGF5VuRgl6fgiyXQU/z23wAsKw7+BigT/QKOGApSMljab5f7AbUj+plzllQ4E8FGkhlVyRVpIdJqghfJAkQvkhSRXhVBj7CAW9FkiOqEWzNGUtaidr5RfaBub/bmYjfwJJM79EdgEfg4eEaEAAMB6BPPs2O//q/HYB92VUkJL8Urg0/H4HTYyJ+2LgjEo18KWpT1N9hAfd4zOXYf/LPE64nliTFJB1JHk3RpUakLqT/in9pkyU7Jef33PfyPQULhTWF/yFRyTbyLYqP8mrR6eLb9PuZfnYuJ4/XzW8WnBLqRRqxSPydZFq6KiuQ1ytClBZ1lPoJZFx7QafXq407jddMLNO06X0zyTxkftOSbbFZHrVGWKuth2zptrfsUvveEk/JWw6WY9EZ4vzSvexxeH7x/uO7VmopXfFn++cCoYGesqpya6WkOrl6TxAXfKgGXzNZW1T7WH1Ww67m/7WdbI/qpHQud4V0XRK329D9cU9Dz6+9PX2v9v904PogdfBXg28MxQzdHZobenGYNTxzJD3yuZGtI8dHbo9WRn8/+sToU2PssX+PXR4PjL8I6AI4DPAaugAKDot5DQVAl1eVwZccnWdyHRGjawkQ4F5UvBAEx6t6wVEIWEP4NfPUjZXgGU6+kMj8A3YbBNoPyr0wCdhrdK29TGp//oVFsP6rZoNkIqlkX87oYgwQERMr46nb5xayrFpWtLZmADwp3nxDbl9bw7VrNbwbb3cyCZJLEckyPEWDj6zcAzj3x1dfvgdnpmYjnE4wZ2aaZyD274ueAJDJ9p8A8UDJ1GzEY4/B1aysqwmexb6DpqJq5p8I1Y+2EiwpxdF00gndLhFBj/DYt3BkYpZG3wtsfrQyeBJjGF0BSMBj+e2sEqVB/gY2ZKbCUvPSCZ6//es9Q7W2b4z8rRGJw0JFKLRAO7ZDKoyBVBgHIzZEaJU6eb6selHmUS7kyezCy1k0tIsD5ZSgD4R9252ESKUgrjILVcvstc1ZL89y2ASB13yJ2vuLoPTljMyo9bvr5SOOFcqP9h6M/iZVyYlhBsonuVxOjXj7T6k5KCprdIaZsZBAIL5IbqdsTvgxWpPhwnK7bw3HTOLPVM4ZlWYTZrRZjEoMRR1Mel/5VmvRQcIu2z//ykVAVpagMp6qoqBWoLIPP1M2yP9eQVfl/6/Z3bJp+5Qm5DSkwa6024uEqVbKGPp0FHyBBVSa5EWvCgI+tNRRrDnXjpEkJWud6dxq2pzmjRxm0yiZyWlXOXyPYhsUUgp9GXKiuAppkEsRbQPTfMgtXQINIZdtntvUSmBOXJiTfV7Z4ev4DNgRqn62fAHUx/9hquIGaDpNYkr8+zkrM7a0TZ5lp0xEDlUZDj+Zyiv9Ogt8xNq39rJ+kZeYpnLmUEj82lJFVaXGG/doQXqmhm2e1ne4zCVnaN/UbwkYBCURtS+VuaamS1DkaOS8W25fQigeqrMVohnk3kHW+KiJTqrTXrZSZ/sfb1Sel+Uggykr8Q+pRYYp/QuKw8aye+LRSramkfU2m9EYfWtGGSPyaBizuStqN5LnGTCOdVSYWkCUVkNAjMlRjUFgUBJooJFFbkrGR/1gMhetgZLjcVf9slOTmxNVJ9HzgtCgYxqXLedIM+pgjiMuMpIoTXMvSM4YQYPCBiHa6GeIkYmQFGlp5mGyiSWLu+mHkjkmIzxiY9yaMA5EBppKA9xoII4RfYQpildYpCpGKquhCXloUrxCE5hlUQWiIWsrdv17JGJbx57JJlZQrt0+j87cRhBSgirwXh4cFGogKAMa26OiMNAi6Vuhk/HjoiT2MZGW08LTmh1ejsHAK0tKw9IU9laMM8EyfWpUUGfmQ3J9oMz1L+b7v9Aq0U/qi0BJDUtprerXQCEV3U8UbgWXNYeELbK2xC5T11L/94Fjhrvkwd5UXRE7EySTg/viA8tE0rwm6wInJkpSBZLmgVoggyS0oIUtilRIUqRCkiJ1A6nLhjJk3JOniQPC0JFIzthY5dzuTKSabk1L46dy0Hn81Gvub3ySbj9oEXoaVW83TL/LU4w1ASFRJuhIlB4790UxoQENbOyAbJRsLzdta5KXVAjNDbXfTlO3Z5xqFzu0n5SXE/BjrVm9oWg/bBG/SA98u3CYkBSMMLd8QPGEAPOFXsOKcH4CEK8pCp64KVRMmUZlDd6mHkkeQa0bFRqsU3mjbAOzDEunPLh+JqO65XrBrSh/3Av7bcRph01D/YroVWhvkesA5DzOdpyVrPU1RUh5ID6vnYvGYqfYnThPWADg6xWwaRr6EnjZQ65fFOutqqoc9zYrPT8WODfIHa7+rOyvaNpx8Ip0g+NEbAf7xZKICJVpM4Trw1SYnGd1v7Rq/PT4bsOLyw1MfF6kCNGsSWZauu/TatyemPFy9UgBu7+7s+b7yr4rrVZXi7VjkaQkJBnSQf3wX/+m4rA6yZXOP3fqNe4KV5XRtjYcCtsRkZx9HhrExFmqW4h0Dxr7ggIxAAdqVgE9BegdG0QnkdAwB4YW1hpbQYfQDcPpLIk9mNpgRxxmc3I9kFmWKez8GODAgPiA0EZeQKMn/QyQs1bFzctmOCih5rEz+Ri9B73iaT8NOWKhNoXWwunkxXA55xd1gx0hJXXNBQT/gyEdMtUAWCT42AnnQ1NoQhObFKsUrRto1aYZQ7d47cDT9rKG5L3y7bKuK6o4XgBFGd6eeuPgxIf03HXlxRfcusH614WZI29DXQwlPkwxq3qYm0aBHHTeCq4aKnMLIeQA96WNFGN6j4Aq6jr2go+9Lgc5o2JCX+UAi0kFsYFbEk1JtNXrwIMJhXOwgAsDMZAxm1mXZ9yWV7j/RTh272Yq4ZnFt6VYftTpwqNaUghIEEPhiBKMw9/be/C8gOdQSCfX8NWxI54pitgqMdXWXSw53xtCYXeG9prfDrjVNL5q6F8jaRk8fM9vHGez1pQVKWUugWVW43PNSRkNa0uljsSTgLAIxc6bQswJ7zUJM8RUSilLYQqnoEKlFFpUSiGEKdHmFor3Jkj2t4/w3wtLQVkJBvBvNPHKeiVBX7ucpraLDnj7o96GbJsz0fmiHy9d8kpdHfi56NvvI0VOiTGt8SX8e2QsaVwdmJClLxooaJjaVkwQWeSeT2n1QO8BF4f+F1n8AgF4lLAceDst94x6FcGHkRFTdCDYH402xF1uIBsovQmLoxkUTO0AQk8T4XZRSB0sjiZ9BRpOUAUGitCKx74+XtdYKEN53nLHP7ugW1g4yFkn6QPY1mpreDdlfB1Tu2mH3H4VZwKfj4B386hiCCVryCq3h9JpDyde6xXUIQ1pSGMa0jM3qndslxvKSFBC+B6qHKRkQDi6DsC/K7UBqfGnbXJwg+hCXjiDfIPsdk7y01Pcp2F/X3sLvlQAqqhUaFRgVGDmjHkBStsGFBr1LgCjdXQJJMiESOx7OT3mSkNJ/3ZVcEhwTgCUM8p0qTc1Z7acfsBYsPuS56X9fFZ1CX53Van7X0/8SbUbXW37tq3VYwG6QctgbwC+8IHVF98r9d5/YND30Ye38eHF2++i794Zfn3EDZ4p3LUu62v/SB86s2JkJqpCycMEEk8RmDkiIROpRRZAJi5AAPRWJPlQ6/UymjxyQAeoAIH0BDYTPrzRQ4/TgAk0CacKIMLuQYjHHhh/G9ILDkw+rPsZqk9fQYNFRSRivrpgFtrQoGQlCkGZkhXKUKZkBTLZmDFyC5t+MfQAEMKZeEbJ3FBoPiojvHf4/ztZOrG/bjn3C1XbLOtF7VGGrgzfknpj8X6OwBsxfRgiJl81GbdMdcFmcSOtZiaPKilIgc7E4piKAGbEorImBiLK9OAY37uH6DS8nREGCBAqQ+w10YByyQZIbwd6DM5Tq0baq2Gaytt80gN06G5wbTxhFli5i+kirhRISVD9lCAUQpUwvJeRoAVDoJ2jn6Ou+PLtFM6XXsXs5e1/9BwUIkVipspMQ1l1dJxxjNbc0y44gy6AFjz0UVUxxbD3qQgTz10xCzKyvnqMpoULnkn+5jIdwOVtZQTnaNg6lLPo6zuOVLQuSSfcGIkl6/9IzV8dpG4Oxcs0BGCRerSBKUD4xZ9ShsbgP5xyxIIbzzw64w+qHYOaD4XzfAHkbFIwGjw9Cy2mFRFbZphH2R9jIaaouanmIeCGEJ4aSDtqDe/zngyGwRsSxOy2NsTF8vYDwzRRhEXDac8T8UjLmeoPMNKCIEmox1hi/6wpVK9Sjs639JgKLNRnob4A9VmAM2CmEDeDdNgl5j7OqDewkrguiF27tqwszyFnldfSm1E0HZJiyI+WDZJehmJuBxkUZEW0UlKwAmGoYzlJWMRYEmhHtPMPvmQ/QpElyGSnm5N477T9KA9U9if1bEwKURBYl8e5VIFD9mMq0xhkVGovShTvub1qACH+ZNy4dhUaBDZlZ6HziPMKsFjWRae4UXcRbFKpApFigcuGXlaz2Amirng2Qa3PD+gNwZ3lsIo7yFn6iFh50Un2VtmrwpPwvDysa08b+246cuf+ACGuUs+2j1rERi7eX4zBQ5EYw36/u7lHbkdyMPIgtAfaAAx28V3KVVikXIVF6AP/a0qcDZ7G5qukCXh8ejLDcz1Pr3eX+X9K5qCQRzQ633yMai8Nz83/8NbOFTcvPm583qyw6npigN7ao64UXvAtI/QWytxTtOWm6IyBxOCSyWYu9PyMYxde8+CMT7d4RpHMiVxG4KudmwHh/OFZi+f8QdbDNV7O/1/u18NVuDrX6tlALGTNFLozHzqo9qGPU7yWp5Ox3hGR1Gl6n1LiMqegVxCmTgy6QbEWRjnJ3JZJe+JjiPlusnSI4HKzlcute155PEdZqxz7Ip2DvPi4T0w6g6JaWB6+KHtnU0UNvkn8CWOnrQg2IbJFx6/w+a+Dxxxz7C3FRcDEF8+HwzMLCsIJMiyXnYX2fQ4WPzuUxoodQepk1WwNBH4U4HUl8RoYLm0LMiZhRfaYIULs/QOW2WGRsPruqSUXUmonsmZfO3B/Pk7RzGdD6XhBJFqhoSKRVDPYTU2Ch/AEn1C7wSS1FzWJytiG17nxM1MHgdh2Sd5tVdMciAtlUKyutO8e1l4Fhe0gx/V/qZ6L+zV3k22ZllKXFcOwHHW9ImhY5sOyVrUB6pqcDV9CWIlprrnWrKLklG84f+Ft/a+GEXLZepMOyBmF5FpVpzWIs2SY4oA9BE1sjANA8NJxEBbo8tqi5mTYZpORZW4oZHd2CLaHYl03EG7tNQOPo9/due+Km+I1KIIXL8sL2qreeUqXojZ57+WOOtinkhtl8uGp2EK1gRIT0yLZOlDj7yQhgf2MlS8O6hrpKbPONBi+asS2Cyme8662tqbbUVJog7WLJdLLf1jj5vHd/VUqus7C1HWrZkJLRqF0sSNOi33zTipN4hg+jfW3SMkwiJQRoqFp4QzbFMiR+rEicYwRxa7MQnM4PKnd5/KntZiMDpVEPK6yGM+xUuCYw4pZrddVKhxVqn64oFXRx6Z6GMvinCoLKuDk32vkgG6x+H+5QmwbOfussoOQRAcLMNTxqHvZqs73JqfibTONo3LPBYq3RX/UVJL0V+O8T8BrVDqoCmonNbZqsqYu3IeYdLEzzkUn6fg4fBdcYwypiCM7WUXwFUdFyZGT/lI9WXdpU6mtKudHW041rzAleRofljkREKSXwvYlhCxLSZXqMgKEbvii3p/4cc6c+cXNWFKl0jSoKfFKvjsFhXVWqb/UHLZ2IuczoS5TClLCFzt5/SstZVfnDISUMAkmbe8kKtroaAvLyJ1Lr1UJ04F8J6b8XyxNk923HxXXX+f20ypUgEL5Rj/HrAl9W5RUieqJhcIFj7qiwDxAvdLwNkysHBA0KaiwAqu4SmkGIWUZBRXCDVATEaEzJCyEcOijQs0YJs2yGN5A8ByaaWHOewPfKpD1W6mu1btKBdTKVYjNPi1xe4ALQW9WtHQ8MKboy5a8A4yY43D0pvewWWKrpjEY4EagDToXRWIh+nKFPClg60foTbmhKBRuS/qYyRasBSdSRiaDiy0Q3rnmgIGtPZMIMhuA3sdeVzwJxWU+Sc0h4bGct4rQvBKETISLTfxRvTK9w9knTyLCoUIpSilqKtp0gDKl6EAUoIgTq3VUb40FvoVEbqiNKNG7+kuWIWuzk2aFFWlODdrcr3kBX79SAN6Q9w41KA+vJyb2rikD1LR3o3OLYkCCIr0yFg+AFM2GOYL0MiksqxOHPu3ARIky+X2r2FWd5JNDLSNeX/ypgJAeG8SnLsv2BR0S5uyj7S9Rp2AIQxjiEIZQheFAwtuFSPJWZDig13CD/CnXDPqYz8ccpXUUNtD7Eu3jCDogZGnG3Ebe8YroKdusQuvTAYJL0jiqoqKzwKQ4vh97E407Mvnuyysh16+vwmkhp0+T1dXOQCD4z5AKGu1uGZ1+01d3I6RrIQDi95Py8s4dO8x7xasVnLOhVXFMMyRKAAnljJSN1BS7VBFDesLF3Ud/IOFyenU6a7Wo5rDi69Da80a1VdMV56lmkR8EtHQTHxA6bDUgYxv1khydJkcmJy+T6ypZpN4KvnFMd/XBcdlp0f0lpnvaKMi2GtlcfhyfHZoV70MXBnh2PrbwErrq0XFoX9YycxS3Hf8QuwVFumK6LMGjuExR3ZBu5j5FWZWi+351KXCGpo+Antxw0emki236V15u4D9sXfYCyz7AjwxKMv8rEGusdF2xnol+/Q2KCe5/XSqYVj2Vyg9hwCJKLkKRyr9zoNs/ZfgBYsUwUTB7t4zI4bEEonpsOZrDduMSslOWhMsyEvvDGUJfplRS+Et7ImYFJDs08U0V8nAA14d2Qo/e0Nmy1iQhSXxy8Pgs7ovFYRtMwiRMWknjNIAaTMIkVEHywoDyjIgq8e72sVrTYZlw6uiEubscmBCh6SmqHs2NQ1nXzO4iRvB3kU2kILuhwO4V93HJL/fuxW8NOpk0J3RyQ93svo5bCP5NeTnUGnSInDOBDhlSkiycjTiARKCeqKG46O5CB5SC8mU6Mvl0ERNVi2sb/DfXMzESySJmDQ4IQRmSyXdn6vCEO61cSFgMk2ljVJMwXm9dwjzqoU1xBmUoY3k+Kg0UqvNR2AHAiCQlJ41NVKEnF/Nbb7/n3s7/saTB0e5YsducQ3jgdfgO7e3/ur2S+eI1YP1DaOuTmYnfSWbDz3sFww2vGDMSSazONRHYdCGzbl+vcKMCaP3zRgNpr/POT9jJihOhJ7e7TlX4ENyn9qUOJAdQu8Jdak+4Rz0LjIpQS5KwP6vDthrNXySLiboCIfCxTg68SRAEPs7JMMXpKNKNqW0l8byoaD06IVA5OqiMx0ZTTFU2g2Ysy2Z/B/m+bXq2rFAMz8O+3a8tS1VZdfwwyBLI2eSRDlTdVy91h/W6VipjKlUGRwllA8Ei6mHriFhT2HneRJQQ5j0GqxCXEmOqhOgKh31g5EJNiZR4Fyk1NmFVwPaoZFmrD5kYJVjHlHM6QaDBjkocB+3VXGTa0emqbWud/P8XL3hY5Wr5o9ur2OQxVTUQNmqsHvxs+eGG1Qjeegx+E8dJTLChntLJ7UL7Mb/zWrL/DBFhkVUFDN417/AC/SJO0XybPV/LDqLItkW75C6O7igx9IgzgvPoTrpyBm1EeDGEou/LnpR1x7lgPlrJYJcELixOSoHTIVB6WNkh9/QupAysWyzCRc9PYtj4WAZ7utkzUJ8as9xw7dIywpBNSFypt8E7o9F04Bpv6LXCbHLBK6ERgyK2gIWYYcSSsXzDxNgirMulFAh6kHiQgIqi+lH+Be9skAuptU8vcZ64RY5l2lpt76MvOeXFkLNaWzT0KIDXXMOcLjeFaVq/9C497H9+iMJ2By1snmnUpMe/t/cQ9LYlRbw3IL0u9wvDJyG/qbF6bIhe916yEvpTj2jOTtEZNrY+1jpaDlMppU6luUKffO7b39+fmbG6nFHK8BCPPIRdcHk6P/16M9CW2KDu5TR2EP060L6TcIwZUo15/8pLjlQa6hxfD67B1DE48JeRBQIktq0ErMI2LItLj2ZUxNaj6vpY650F7wi9kVJqsZYuaJdN5ORHKPqGaHCSNk9BBzmfc2cvaYReK0yplU1CC6UImojxoBQaIQYxSi80TiGVFDuH0RTCtTPOb3bCJoQNM/QtgZJBLERxWFlTjMamhhv048FV+fjM+LyEFwW6KXqy3piqYkFYxbF7VIR0E7mTIhxvkG0uZyPoikWst7LCWcIO/Xx1JxzIobC9l0FUivZ3MrLIWUqFMaV2nSGWxnnvYibPPD+o0vxw0HAz8DLtQL+qlHeO7h8SNcN7u3d6tsxTodgJ3pMVZM991Cy4TbPZp23RPvwd3wz94HI2RFSDP6dsT8tvLuJVkxhc3q9k852+iCF9+hZlbHo4PLngdOvv3ORB+f91retFXLmoj5+V63aV/ScqQUW8dBB44ftb209ujpwH0hAeVlaRaC4wFtQ3EQsjwZpTgSgvuKV9mKXnsl+S3AZe33JtQxW4xWg/oXNhvBRMIUFAusFuxYgkKlcuIYdyN7QLIDYNE2RToyQ5drfDrJ0Zla07zSWXVUiZCM3UY90F+aQ0pmmkZ5rvmKfcKVe3q7+L4c0+OGCfUuzz0Xygkeypojjgwgp0qm7FgCR+eMBn6eliUyhKAWH7Iy9EjIpaKBfOpyD8zSTTUhEhgqqyOTAVbAqE9IJiMZGBNXWd91EIjfKiuVTdJ0GtiIEByVtEmIVHnr4eYxtj3mY6l8awxKqN1xn3qY5eIrGMWlkRpY7SSCESBCiDCiiBasgGUrN5IdaLyE4SxjjE5bTX0wJkGvK9b2lwzJHZqneWNAshS9OsKFZPovhMAQYExBGXVKbZiqbbrmWaloO9EsEU1zWdChEkP9a9hR/3GgHFkKkXSZQpoEeuZZSammYeFiuhtMZUJYJLAhWgComqS6AMMlBJkQnV/Lqpn3H7AsHxxlTp1iWiMyuXH7DZCUq3whWS+pIyeZFgiUKNlKF1JOg2lDm6vcQ3a0Ft2XGGQ/UduBVW29FBsjxyVUj19lwZbKv4qG3/Uzz7hlISxpEkDCnLEDYPy+U7UB7T4cq+OVMTNSMex+f1E8CIjJggyApyGCBDEqQHBCYPAjEuarDFcISoD+i1jamWyJRaW/zGLFoOKD1ym7LnI8su8EXnEmOXYtEEZD4RDfjN3zashKch/gQc1cwKhpoqDbETN1/Oo/4JpdJQZHWMksI9B8Dk5XC/HihBmNYPVu8tKLQMiDwZLVHjC2igdzeixYmc4CoxK/tkgwo2v1sZTmsnTGsH8bwBI7R3+Au0QIwyixEognqIQD3UA3MRpIwjZEUNb1XrW0pOYZbqcDKzaEoZkLzYSTzeWBqY2SMy55hvZadUw5YtDMtP3Cmg5HmrSjiIWlQcd9g2raxpAyNFR6bD1Y+eOadjTIhUAiDkrtmNyg4UQ7b+sJODTuIid2bXG6uR1AnuwjTxIAC2V4TXQXhhFTJYA68dHH0zGAjEtPcWbSx7kVpnWTGyeyNieZ7L4vXuUYIyu8VXAKF5QEVjv3c9d5UpuF6urznrwwYJO/8/lClkI4PBSRDnpXaa8Ya79Zvya9Ylye4f2Vwcr/0+x92mZJ6KyWpe9sfaVY/Vwq0KjcHKK4aZo+6Y1nu0+dI6eoE2oPl3oIrKcyr3gpguQRWfk7tjwimm6bYk7EsOaVvOVS28pabYnZAhRVG5+EB+ryY33XAyqKmY9DUQdZs8hxd/RPY5htuzYym6qhfJoALzugGg+aQAio3OPabyaJJmF6C6SSLwtvtCzCOizEtZcs6Zw1z7epniSqHcltoFwvMsWWp4Rvk+lf+73tQfo804p86t2hGVbNx3wPYGe46Zxv3Sk1uNfMWRE/hgDkikRT52h5TwxiVeo2FNsPy0Lo/qs7aC3V0qaifL9x5JLWq1jexz7HZtybLEIs7KPM9FpRVZ1Eo2KbvQCm3YAhFohDpohnpoyJP5cpSdL0LFMuzftTdTDh2F5Ea/1rJaSUiLno7bem7RDJJwRPPJJx1nuSSDplQaj707nlvDzWPXC1GdUoHv72BHYbs+dgJt9aaBljadYvVb9YEqEyLbsG6eraSOWw1GqjkTY1+w4WjPwVVAF94zZnVLKxDjfYEBCN9LV7z16gmtNKkiakddNU6ZvUnxSlVFBvvn81cEuH+v7Kz+NKePsvtXupIJTbGkjN6UlZRbWaTpp8TLBSpzkfP0NlMjxlypm8blTEAUbiKp2WIjuzA+fo1Pa/2nA1ytOl9GPsW8Om9EYRSDkCXhWBFVfNtxVr2f26XyrOS554qzh1HwwqZqR0ah19TkizFC3K3NhH00Nh5ORO6TyCpWIS2SKWHompSMvAjJ50M4Sy5TW/MjlqBay4mFL8eyCbyySY+kDjnZ4AN2ZXZWX4MR5CAhb2EgsvrkDrB2zrxyxBJLpgJmcnmz3lxuXjQ/nOmV3jGscRvfKw4N8abhhK5OwkwyQNroFUSHSFVLhCGyGZYBEaoUM8WR55MW9w+xN3MSt/xT85rjpYiztXBywDTh2kziPRYdEfQAYDAXFlJkJWSC6TATplIkhakgrQKRuWSh/awqvZeXR8V+Z2SFX2lwqhA8oUrmv/JSW9hPHhzedHdwPE9v+HCmJwhgGVDNcBFharvANY1YzTwVmcmo0ud8SlHmOLpeakWIwzLQIj5O8JZ6PUcHM1CEN2Pv4Krueta2S1MGUe/0nJCDijSijvW/YPRwDe7Hfl/Z0M0AVsu0hHsqvqrOG04W9DjSxoMBw6Df5+4l+vqFQxXkvExG1kzgGCqVTXcm2u8ldGNzkC6V0WQxF28sogrDMknZ56PNsZeei1+pQXiwxtHTAhkv/B0egw/Do/gIPAgfgIfhIdgK58eVDysLSpFEwTVOOz/Zy//yaJZZR3b9SAin+eEYZ1QTjTtBn5Z6oZlty8E1Wkf44nzxZGqf5cyYUi7ThbRTyKGk49Yt+4y6fpQKYiEl9vyklLaO5i87z3pCgF/tXlrkmKr99jx7m5oGhjuW5X5PaGvzH1K+eLJoeXl2HaTm+dbSRg519LLnG50Sj1TlYiLvfKkSlJIyT3U164UehcMxhNYkiNTS+UqwudHezOqVJxazNicX+bHNlvGJXt0wKnmlWSprLdlNe8snSDbQlfeZIDklyC0aQdX3wi611hLOcr5iilJ9oBxFiEiY/qm0Q8U52Zl36G2hjyfShDn/yYn/7ThwssFIhkWFg3zc88b2nt+rLNWQrZ7Cb4sceLr7sO17I3jEBkU8SHcmdZre4cOwfqXOJVlvkr2dSsZVjgrV55OWaJZJabRfC4EzSJxaKVPMqkvST/CRfj0vLTTvxYNXV3FSfMsHVai4ddptdGMKsYa1D5m2IudYipDI3ig5PjY5PqTLokpLPGoqxvI9hpWWPoll2tnPQZ4VcmfapXOypkESw0Al36KnEmvDlD8yKwz3C+H6Dcmw0ZU57Xorx0vospyjLnOCPDjMqaAOMbCk+fBB2fbPV4/YXB7dzf5B4HLrPFGa6EichTjVFc3ftXXENky7Gk6xoI3zMrwtadfMImFyhK8L4FQx0AaET2shWkuJyKgCJgRErSi/0iDec+WPC6MiWVLkQNfUMgeKsDEYvzzqGKX2GhQzJ3XWvU+FtY4PFDV6LTlxbszTPq4HqvS0HJzCx7wsq9rlUn9voCt5y2zLMTZaDfjB9mOHfFtk3svi1GPvbfG01d5COIpVhzxqPN4geT8NogzicYe+HFU/T9fW+KvN8jbNRZG2OvAS9rFXWQUxj+opQlcXe1KUEnleFhfhcsqQF4ac/dPjyXg28cQClOekPperlzsVpeIibHi48wkmgqu3OxeonPsS6qXUDdO2VHQZBr7bCDtIl8eUqguGTyXo7nGHRBlKfHFocDhAYE3Mh+E4ZjJq+I6P+6HH/dbbiIo2L1MtUkqZGY2WF3PgbObxugE7KtqSPbv5pltF9M6wXD7COyBGzidzZ8f4QxmC9Bqr/vxbBUXjsiWSakVETeT05RSveZcY4ryBxWpC1hFZd4SPNmV2VpoCi09Qtdvwm5dM7tD+HGHSmkmcioVc26Re3/VjeY9/i8ItCkD4BeRCJmRiFmRAJmRAKkU3yHSjOpwq6i69yDKGAFfD6oesN96SpmsoVXiGMPaIY4ksMNv5WXxBFHiuaTkJ363kfwCgILKXsGFY66cpryWcEGtGfVgGTHEUhC/xqpJHAikM+Ua/IZzH2WFR5A4btVIBreZ2b3SONg7sJQbvZYMtRqeFYd20ZTzmrTf5vpI+J6a3OyxAN3LnR8bsxgFHqtRG+aXwOKWWGvdlqzNepp5UX8CgIkqO1EvhAUnphnQJoOiBI1OKmzzgFwDXVtR84kanhqrElZsaWSjINiXb48hMz1TEOZfKiHpSJXEgwlkSFocP7U6Ipi01jx9zUoGlnNHyTmYSaeiQGSK6tLI2cfZMTL4L0TsIgSAYkBC6HEczBKdkDKtR4SoYfImrwQjPqhg9CR4YCeki88lx0dCCMziEznw0NUnQqecAOrxXoZn5eFzXXMIDwG5xhPrjdFy3G+3cNEpqrhteDIFePROL4TXwNlQvM9j3MOnUnxx8qMdEFq+Qd2lelDjxOxsqOgkbCIb1dZVacN0vNhGwNd3zm8PiL0kA0yvL9w5dkfCWWJKau2XTiORrgWqjmZhBrLWUhplAPagjK72o0Fo4yNblx8QVe6jPxyLFK6KsVDfgAmXLlv1U+uNaBymHQocdGWQGJfOwQSeRGWi6KvV90dLZ3bnh2tb+ntijAkXInVp2uPbxD9E+vyMgR4Pymkrwqftz6r4KxsY2hPAwINgx0wue+GwoGbmsowdU9DZ9KknxRE2hhItilKKUYtRijNOAYpRidAdw3cBoHq5ZCdjUXpqE/UwJ4UemCcMg5eHRqGUGgECSIIweH0s2kyiYyLBQYLAAC9SqkhWiMgazVN7gMrQ3ZC0HdKCsoJkOONWLEuBwbgBdVTEduJtIn9IsTGk4TDzL4GPm3kaS0e4ZaKN9LlqmRKzbjOpMH+l8xfEMgQEToZMUdx7obQoaxeVpVm6eMvtKBsoiueUZtDiCx93ZQNLyLSI29QHo8xQ8Gb/iXciLK0OnlWwYRS8v/w5VvCpviPkLdnPdlM+TiiyXTqJSsN297gG41O4wtGhZkSVMShBG6jt0hdY0KTq4fVIxUQ75QUb52dQ8olZVsEye5oldHHEnjIDE3M9D7aPyVrCjElA5QU4pB7KXytGxkSwFdA9zUH+kiVtf3InbUEQmEiBMAJVKRuY9KTRLPwTHIyqllK5Qx8l6BFCHqF6BDUi16QjgXV2jeiZVQgtbs/fGYMAtW5JkW4EV5IjX5Y7qVFdDS3jbVVlEiDzhL1dTJqN5XleK4Z/nzlmSBajhucvUu/QV0buA/58GJvFlZtIEY2L3/qPPfKaUaoKihtJPFKJp0evI0YHmmsjGTCJQFSw2xKJdHNZUtbJBeDk5chKHPbuONiAmuwY7x+wYNqRLkVV+3wrXCUv1RBopPWxVb1q6gyhkX0u44vra22I2EGZ7sHee6uYQM02Ye+muw2LXNvi2Q1edxx7PBs1G645A7yJQpxGOU4ZB+7VLh56FBN4CR/A5fIqfw8fwGXwE71OPgs9MvDvEa1B06MuEJJ9QvIQvYclw2mVoYCRBK0XmMjUto6oK50q4Tuf/udMl9s3ebjkrl3Xbcf1AcnJCPHBFwc+dD3vZdU01AYKTCYZx6iyyKMKP4/i4lvKmlFwECetOjpMeido90NSbJMXSckeYk1GoNMZHlmFhMJegHsYQL/WK87jp3wguM3WyQZK5++cKuR2i/+f23eaR6NeRn6+YPD+N77Yx3g2zovmSYGwxiXHQK2yUDmbUquM3tToVuNzUSvl5doQtzhKLUWE128oqfCgkfGIUznjKps2uxMAcv43LWXeLT9JsxgSmsnAK3qiWux9xFq/elNTzShnTzTik3ZQmAMH0+9j7ClkVV+svAmJfYsNSKRGS44ogPV6KVU2e0FWEtmkBlNmoGFQXjGGEHxQbEZJEcAgcD27KUnPRBiZOnlR3wzrmO+xzz1ukBKWSpWYyowWdgnHzuzsey0jUHRIg/PaAbhBcEeezjXHBshgELnjFfvZsfe/onqcWVhAXjDbCQIlzBFq2V9Wrn34sEdNwRRfkTUkk+SzEovBK8FHZ08QarKh2CAG72sZik0ATKd5TBUIQ14ZyF3W8Y0Xs1QH3siwkmSDjDyRrhEcEsNKwEjfQIElzPKaBFRVPIe8kHyuvwwqLMH4+a+qwsLKt0kAKsACLO1BSbJkWQ+KCPZajMtKwbmtGEKTKaJFpE8Yo242LdGM7n5xTyHbVsrEHWZqagPz6PwHb1j3Km3bGZCk12Y3c10NjsoGB513eUPW/8/lSvVEt5qlhU/cmXcC7jebd6SBRKQ12Q7szeF/9eTabBTPZ7WbRsOlu1JzHJV2WZzKnWD0RdWR0EaIstM9nc7HIBOdY2BtSfsX/MDdJfJlMxg4dRjeVaBiRrOtmNpjAjumCCHY5oqDgYWiuunGyorAb4vrxeNSWq8VZBaqT3B1Dmgia+kmFB75XNVbJCpMnPz6ZuKdNCE/0gCaU46UDUaXmJEeuy7kqGhfAW9SCwQHpH2zBOhzAAVU2as2SxnwZt6M9awdoyDakVDLNcTAvtmsjuZFBKg4Sb0gZ5ibmUPr6OYRse2rpxApdLyJ6Arlm90JNXCOXUWbhyGPoAsge99jls+EwauY6BlmgX46o3JRGES47qojYMrFxljUjAAOixdz5pKivN7Mmyyzp7ENBT729QQXvNB5WHuTP6qC1E1YIUFdmDkk1bJ11A7i81ZbyLA1bx2o25NGFgE7kqx5xInYqKIrY23/IL1fNlWmJ0ccnVDoByR3/jKxxdGv7CetN5wFiX4s0wDHYABr8MuRjIhsx0EaGZBtmJ+P4WK9bS/xhT/x9p+qqY5iEbEsUuyRa/h5Jlb8+Zc/96poME8cmoe8o/YYDlpnf7sgYcJEXaHLvyakILLNVkMW6hUEU0bd7NADveTTVZA2oGIAWuzuVX3cXjp1316SDFuVZrtT1/S5YFajlALBggU3gpfAxdU/4gNwFdgdyF96mOlIPzFmLXOSQMBAPom6XPXTQLPGag6ENFHHNRzP9vKbUo4MnLZX/3xCbqXZBX5y1FNX0hiImydns/Ey3eCzyrJ0oJGcoSu7MUn+eaT/Ugc34NlWJphTlDZWxKKDQ4WzgOcZG5HuzFDHDReNImotFr59cENqQsg7lj1IMzBup1+63CGPc8O/9/cdnZNdWeD412UZcbm9PknaBkvjZmpP+FgDOOMf/sNrEhHAxnI7A6qXgopRf4Ha7THK7XBxidqAwNrMYdSXA8gmuDdRZw+B1cG0OooMjfBXwIlUvwYc+Yr5Xzt/ilJMObD5+fqaYU4xDVakFK1km0WofSmm0lYM8Ewp9G0vRPe0kkpDkQA3L1utq0s7wFA1nJkem9Q0yTzYwxajcKP39hhiHQ0Cd45IVx7xNwkW4hGQXwulap0rZIEPuJLOKxyPo0T4YqdQWLbH/b3tJs6c2EIV9fT8+yHtbVYH8JnFQPQSCN9GyR2eaPzLmWB+KabTv40iGnRFep89m86S01YfdAE40zJug+0Pp7fMbZMuxDppcmgw4+ubxnMf2rcw6OM3j1WuxgkVW3AFlRHtQDDGoggaoh5YJpML0QjJS6b+yziHagMciBTaKCM9LT5Wwt33WcVQ1zLl1XdFszmVr5VvqWiEqfhKWY2lTudBzygTWeQhP1tIoyh6dIbX/nMtmu4gSaCGp/QbMvKYz17G2xuzEiQjuJl6jsynj6RqSLJOqoxewrLMYDqsADDs/sQ4ZJlKcWe3dFSK6m5GTVJWiWvjzqpJfbhYSLbNRJmD75GWLCEZHf4wQAt/l/m051oXFBfekInuvmV8A8kQysQdmmOfpO2vJ/lpfm5TPJkHneXfAqSnuH+VwzJ6jp7vjFyW1R3hHBPLsk3b7VPOc/d2bcr3NenUPXyolENfxnh7kjStlsvGd2xHjies66BsaX2QXv3Ul3+plv1j1bCJPknlt3mcUSQzfRJqeFD/0a4bc8jKX9719bW6507XLOd8KHTC+ABk0Bcb+pxfnP9phkTK6eWisc2t7xcpqR5cIZD4h4m45kZ1tPIKhUFkJb/BIx+jwx2aaSjnXx9Ked09bHLQxZL/JehMEW83jOaISB/Z3TPM1vAMZmIFzcBbPQQ+GMIQeDGEYU4Wflf/jvrXShdB3xckzooSWxRQhhU/m2e3UZN9MNbFGlkq22BXqLNatdW6jaceTRZZTeH6WySV1Vg5hqadUCTSHhc6yrXEksfM4+Pf+fqGULxRcrJB7dKB4LbdmbfRL0B6mKubqUcovcDg8vhKjkdQ/uuCiA/obsukU9R8gu2b7KRo8h3FnNaGPRXFSC82PFHbw2BhOhNpiS1P9VyTLSELNuUZ5CJqKB0VwmeNO72lMKtijMk//D5crTDbqJhVGvlNP/gNb7iCXX1vij0NyyTxiuafIvHLEBweOKruIyVIhMwlPFjmmTnV5Bq+qkqFvNNn5oR5qERTvDwj3qF3hLtkFvgPfwDdwm9qQ2naRamd7GBSYG0CiKCqdjBLJ00CxEVoVAxY+b9p9OJkqxWDzsaoELqtjPxnGfRA19khXFDXOiJd2NSuIG7teUMzNB06FganrpmnoRldZpkhKkNTV1fh1hRN4XrCmuRi1lHZHmso1Ax7HcZvrltIIzpvnG9uL3HlrW/ot3zTQstEgDlsUMyFHNG5Oyf+EKmwaVKggas05tJbch3ZMKVay2BkrcqSZWRcShcYti6jzlJVhii1ieRtxgiJJ1IY4HBd3Fr3o9x9/QV7c3UVZ6XumJgXKAuDwUqd1gXRdPCiyzUKNqVfrjQZrAinOhh7zm/Uam3PBiE8ELabHBQKj6mSJDgpYjRK0w7x1/aRl7Xxgwd8W+jKzIonT4jGVwcKUNhwC83LfKBTXHNjYOsHeoi6YIy3ycSghZZlVnRpltmfKeNw5luO6yI/EkBJSHhV5qbwzsGZMJB2Nka38UC614RddB1Noi3jP+CSxVCrrJjdYCCqNNhXWNnm5Kw9KUorx1H4s7vC8jxTXFJarumghgYlonsmI4ZEitZ6x95dIS7RY8EKbZiU6Q1ZjyJ6KW1WrHufKXAhMQMZe6vBeYsZaNOOm30jgoiJjRZLzVBSrrIMohFCzFV2IkUyS0PztBUuHtNxzklbzyFjZy6YW1jJhydIm293SizonA7I8oZgHuS4GfL8YxdWpmQ5pVBVdzydCnCAhyGaIaReB0SqvHEJhNuzBOBrPslSP24TspFg3LYBPWfTy1/XbStLEaDaL/V2bzSRW3fbAa5VnFgihart2cFZ8iIJFqr4YiF1RWw5TyyqBne7iJe0lZN99ritNkjLu8VhgETPSsYREd0hqDCIhfFXt8B7JJVORQ6lLn0ELZmAel6k5oxJGzRu0oABmDovpgwghPblJMLAZ4mzYJG7Q3CdtfUKWNDog6+zjFI7luhzHmv+JjcrO4T7MqndTyD9CWE1ntq5mx0WWr8hrMzqHFpxRDt5z7PjTlUam9AuJEs1/cmnn4LHibmSJN14NG+HV57gygY7dOtaLmEn3Ky75+DekLR1nXgvWxdI6yb7wTUzL5KqNqaZzzsKbdVO1z5iWK3cmatDKFZK6BtLREPA2epMT2vkts7Cy2XsCEIPN69xsO9Z4zzJFWyy+C2+4ALQCIy8cQip/UqSzI+xJa+DvQlK1lrmpGEfHjrZxROYQDBNWZQsPb5DrimMNTz0VFdHqQCIYwfIO5GyPPBMbniOHKpdvYuK6SS4RE60d6kefvwA69x6c8EX+oCTjbYKiW00cUxVlKIj1Rqob9ZeSZ0UDjSCBF3kgJlqHswALiW0eBsHygymMzN6IM4WoiY7MleZuLRaZ6L/PmmOCOxk0h/cUdwcfYVhMkgYjeb2KFmxEK+7kffO8yaREvx1GeKnw0NbXumMoSwq/tJgpYWHkjNNMcTkj/02V1iQFSQXi0JROGEVJ5CIJrCAy5M6NdTM/TC1pP+BZFLe8e1gmmmHPY9CYdUC/lEbWr7vDiCOxV4hJwbMxHN3tBLdOc73t7kiZEXnb3QNK4KhmoZN/AjRPH2UEUIwXEb2WxwQlKOd13gkJ40V3SJN5sWLwHV+iadrY3yftPE/ghqAtF0+JoTZpGtBAmY8MA+GWYB8AhnDlJtq8iE4YtQ3ctsZ9zEnVqgr3d5Ba12824LoR6bcREiVuaJA2b3Kc0x8fQtvYWyqrBgvD6/P1g+vLADPkWhsfly0ux3YS2mWkEnEuXUCqUumlYbR0SartWKI8+ZQ3G2o5hAGoYiXGtotb6qQOVnCajCYs2wfR/eHx7peAOkrhHAxggAMYwAAGMKAChcFlKA/R7DoMk0Oy7LrG1INDIqukcXyL2IsmyHTz5DjGqtlVKbZaFSsi68K9NYhtq8KAVPxlue5U21XOdjwoURbap2sh0WazlM5OVwD6VlVd/PlMyfnrmqcYZh/+ZcjvtpDfbiZnja0g8L+InPH+sVqd+7euVNt+DBoNuRAhF2jI77dyQp4qRNy4mhEfDzubK8mMSFiQt/A23aZ8qxNH9iJNyNEpcnSG7ONMnaaZBp9zO66CNfuQHJMhx2RILQxWMc2zBlWkcjZZW0vSLZIeiXhRT7hlv2Berfjg/lQHQjVLZDtpn54XE+RirRE9tvTQvFi0kOeF/NMh/9HJTw66wl6P75r6LEytT4FdRGBmxwT2cWzY4wspKC9V5amJvbDVqaU2qX5Aj949OKAX3fO05Q2euhltcZxVqmien4Fny5BBZ+OUg1q/W1rG6wBNA29DVcoBoLD4F8d0CBrejH9/dL/5jTnjryD9XwwBKTLeRUiO0Fvrsu/6fRSlzMBmfJzN8tjpQAWo2ObM+rPGJv7naNtUomXe/NpAeGgFgVxOAmE02mOwGd0PJ3DNaWI6JG0/PfihJYsCQjDO+BRUjP0+z6d0aI+S4QBXYF0mZ6dBEtkRl9WXxSOsO+BdIiRSEHYSSnYsH+uZCv8ntPVck1E9zrCTspXSlNLU0ozHod4JalCDGtQyTAbav3uITK1iTWOeS4sS4bSmwy6IvHkOMAkF2JI2T97QNmpFYg0tiaC5hcR3U1gunSl0HP/undT4b0vxx7ibdtS/5DhW8Q+kowc3N4Nn3fR40xaHYlQVaZAh+m4K8qVTYHnU7LNYugckRjLdfYfGxaBmGxDdSEWKI1OyY3bA8vc19/ElK/ZuYPJQg1bYRBPxrACDNDciFskzV94EDYRmpt6fwbEhAqOQdHpopGs0LgNbLEt4db+k3etkAinVTPuJZs8GNEu4mZC79RkkD73Gd++N/v9OMvPJq6Dw7/yd/+d840/EY54ffhnvfMzvvlBV1hAAQI+U0NBxlY4IPCbopIg0PXXaQdwVGuqWE3SK2lg/ipPT1b0v0jtUHESEAaWyRjWNuhe5d3AWzlGLRkUGuQ2zlsb1wMYs9S0Xaqu0b+fezIrt2i1ZAu0OZ/v2WNk8nzydVNFmWV88uGmXteFyakqvM4bLc/m9IZUgPNxonqKsSgNTnt1WGxqOx7QWDnguyHGF4WQe+97D4UJwCfKtgzMHacoeBCHwr9z+WuW65XvvlLvOUj3N/2fzjkiug75FaMHrS+mqX1ep6LDxsa47ogFRgtm97MG+1kIb/Y2cXZk2GoO4K/JCt84FTV6t+htGXscD8i9oeug8YfztyrKNcnaBlOBLxBpTsL3Krelb0iu1NBCTDCNS9qAz5CyNzge+2c8tqxRLegi0NKGLNyFmh2LqUGzUlr5wCDzPIaVwXEModhsyhKMVmRlAInMe4YHZAIrZouo/VRujkEoGsTyqZ9EDCxzOoF7KcJhuU0JuUGGRLLLArgbx7FQf/8UFhe3OM93ToHT/G77THYPbPQY/whtC27olhMMGeMMaP2S7E/D2uBDfzufYs9fBAlWxn/vWAZTq54AqGZyDRXWKAh/NJy4DReP4YFIlbJyRhS/+LGaSPKWhOc5QeiajU45632eAHug3ebdnnj+lW1e8h7LMey+nW5MvTIzCSE1Qc2TsiFVN0MJDjHqh3Qy0TNm5e5FQiVJwZfPLqlpfkTT/skZv7jxw00FzgMn+obueLVfRNxfjaNamUzGRoSIhUq4vNcbNNJWc/RO7M0K6PZyrqu9Y6jBVxIBQYfkbgyUR9QGJAX0e/NL0kC0deO4GD7ElIhcHafGJMwWglgBEg2cFBM4lBnlAbFj/MCMtLUCN5IifKREDCoFAAn00YDHXyHTedsgMmnAa29TpgBrwGvoysmgCZFMjLlG0yGyEgDcTQzOAnmpU54IIKieLiMI+eJyfigdkSjwCqBi+b26tel4VQ/API5N0CMJdAfQRZxOu+jeOtb78n834rXShJqF/OjPm+37bBLXSVyc+odYmSjw44IpEDMasnlx+SBCTryVTH6+nbggalnz8ak2GwGkZP2V0oL5MGOga05KAGo8w2A416lZz3WJa2KRyBpsnnTtHCSumTL3gopJrkA1pyqoSp6notP8yoPb2NKpNhunWq9UWU/3/1xT6CkohzRZJrWDPfOKsCkSWM6YwOyy3Tw6CiIEVPsHCXpkxDhazVpGEltqgb8qGqaBKKw7dPaXqjTMBrPNjUWJ7VPH+ulVP24BEwbgSHhf1bN7magweH+kwWNkbMc61eLVVrW6j73aotmtVtqJwmh8cG1tBQ2qvpHW2Z0485A+kcRBCFFwkM+ie/7hyjxd3WvG8KSmt1kHbWbo8q9XOom+Yr8EQq0iHOieqeWrQKZOJbGVvYUtQ6QMDqSKfeJfoGz2UbaermIIZJtxK05Q6Uhcke2TeQd8feDu1kK0YsL8gQBkXpsj1tYWmB0wVkOrZzzMpV1S/NNOF5lMoGhLge8AO5hgKohSIPJPV50lQA65PGsPzwNHat8eyDMMC0y4TBVE4UhuMWjXqcdRhP/fRARSpbrBIdaN6bViwkcwlbpVshjrBWWQM4mvljHZbFkkLEyTfVXbpXru6hEBAXR2jFceA/6ARuhG8L4gaLbYxNjjqmiaO0s3rEbS0+YnVbdyKrrU4a5gSiIjEmDY00SO32VVd5qHAWRKblSEYANlct/ZQmL96MIp+mabKhQIRJiqw5nY4TPAP9x3phik+zj5Eq4ZxeJoNRs+VQn4V7ea9K9IcF7h6i5ecCwRBXItinq+ZChRsX0uvTKN9eTmLFa9BiuNCWDosGTNUKDirgA3IHNwWHdfb1YXkyaF8zMmQIDsHg6OcPjH1ewNXYyFtUZFOF/NGrFpmIIFUCy4hpRbnoeDyvsX/keiSfDhNPbjAi7reV4MrjlvD9E0PwqhY181C7usygclLS5KNIZ+YEw96qon0lhEqEtUBNwQikN+IfD/5razjLFFy1uqmgijnbjvQ9Ouk3g8WKpLkbGrS0uKqsMGfU2a/lHaR6HlO2bQbGVCaY55OnX9JgUTVn0blBNlt4q/ChtPOIcJXdtkrlvcaoyeg4iAhGffa9NwCyhCBRIVIOd8DKD8ReAH8AX/jYe7nFABDE+pQxzqUoAwNqEEOaJmoAhdCpX4GkiC4+vfgYOjAbAnJLA7/2avfoCwfoYOfY7tM8wGCle1ALKG5A+7/g3vpyXtrX39grtgGqwiNBkRO7zZvVn6zYcO1kXueDokj8IwfoL1lNHsR0peAPyQppEKc5/lRlRBKYFQmUgQSQogDUTABmB2RjgmTIJQajcOKQBKolzpYKBaH33ql91p8PWWQukr4kHoYfIKfwFvwDrwF78Ae7OUznmJyyLSi5YUFDFNEtkUvVXSX9QT3gjPj2gRTnyRa2oTZ+MA+tCujrdtwMd4pE95rPMgzVKdDCaKk9lVW583bFCPTtMxxPC9IRAtdt68r7LrDZPMxb0ImTRJ+NgjLu1R4k/U07WruY60q1Pzd6EjPEmgiJRY7cwrphAcGGIbGuidc/ugYH87ziqIQJwvMwaP3Z69IlYPDjsqzkh4tbD/SDotTSTjPr1Sp4Puas66w6iTPV+VD5fXeoKZGYqc8b2wLQGx/w84g24dItiNMMl+x4IJ7H2CMPmQQZqzBvgRbyDQO15BSNBkT5lkqBJxn+Z87z+uiohDiD9tyZu4321D6C3mzJ2suVrzt12KMy6Uo4l04xkALowYwlu3zJsHNIgZ9Yq+U5txw9QpsoH93lCI+ulKuY7lkOacFhgddzxLpI6v54zLeQn5K3oLxUl/vzI41IXJFAV4teGnROZHYJTc4iM5DeBwMD1TVFK0sw6GmumChYCHYI4Q7xVsj2SU3aIrotQO2FqwJn+CHfqpOBVtwK6yBPlgDfdABHQV8XLJoz3MaF1+IL+WsfhYGs+wzWNEtWc/0jPocDPGmgaXw4BiKycLF/hGkgBw393yMXHYijD01qCvT1ZSe7wdhnA+k4y5KuuWvd1YfSFd/vBB8/TJqTsOOzjNUz2rwKpmL9JHaPjGR0rQomlNOuVgVCoio/oiapQl9U+c672K0Mk9hayPZmjA9a1MVB+cwFrNz6cwMuxDbjoeniiRvcsjFK8WdI7n9g72jjippNhVhv9lfSxf5XvNRTSFahQyGkkxLqsqskY9GN0vSV+u2Hhb/d8qZtwAs7P3NZT5Obt0d4kJRb7dny9nOjMPIXqTzBwk9/nk7to5GHnND6TIJdF3lFSSW1C9XFOLYujp6oT2pCjSSIsb51JZNQQXj7i6n3Lk6NGhZBqfWupl0BJe++Zx78Z99ge87No5Kp0VocY/NuCZApAm/wbUwkzg/iy7OiNFVvbc0bqzbVE2SOZzmNem/wo7YaMgbtaLhm6TmKtmN5ZBTmq+3clXNeSQ0TF2PWY1MSCw15YpakbSqgld+P8ku1KiH1o3sL1+Kc55pPq1UiSmOz6JaGTEy1bfH8PeOXRBD2oklzQQVJUiSzRRdEbrHYKYZqM3OJMXfPwEDWaiKJOpLAzccPBb2L/s/fwzqFgXehSds8JzCu0aijvwFAKSU1m1u9m2PmQXTtAmFYWR0FUsDNeLhOva73WVw7aeHICIbomY7IYbjggYNrYYJ9eFwahPOfe0NXuvfBy95Ohs/VC1/EmQx9G+RvdITMnqapxWemu0a5iqrFe1DAfpbsr6ET+p/6IFhzJh+CJE/78SbrCm+Vwqx7wZuyDAFrI0Ol4Li7ClqxPDwTLhOvSt15lzn2fFA6rAO67CeX3EwzDzH0X8ZGhxNV3Axw86wwYjnHtD8w4CUiQYFe6p6BuUbXPAh9vwze6w1SCcPNEW1R4GtCjwbBcjGcRFdMbxA1cmfDQOgwC6fWAUDCvQ9lFHCQNkE2WBXZHEaZMWhNhIgKJmo5qDLNfdN4h0Ars9JETLnplBfA5G0hF2dMTZaC2bPB7r8OFC2tOYQVimGKw9PdbphI0TLottdSMuyd6FpAfLJArd3pTjoiB3krC10BU85Dg1IQ1MYaJfZ4I+J2jcOGeC7fK3seUn2ls0l4/jqWXZYN+3tRcITuTcdXR332UXkphwmRT/aHxO7JcgZceAhWSh6vpheEa3pKjEgj+SASYKg8YMiDw2JUDAz4F5iEH7oSWebdwPGRXMmjesttvZkQgstO2p3opM6pSUZNca4hLCbal8FvmK2Fl0xAbfV3fdzHIZLUlkSVwQupMK1Mc5KoyZRrIYxIeEQy8rUdNXUzIDNuW6pjUWNqmLdGgaHPcNi6RWjkgjik1U4H51zdu8WAm3OV8yWr/MNhQx+wO6ljMyZp0W/TYgsuRO6JTYWimfFuibF3cQwgbyhBlWL3gVHxnWrrCLfOfDzdTrgfhdxG7FHRDr3mGns2cTl4HcRJLZgHh4/BwYPt3UC9+EY7t/ZcWIp52cNSy+kCYPnjAroVuLUWN9j1brbnFXEjo2ZS5xbwACbOuzVXsyFDkjCMFSjKzpLqqbpg+Wzpm2ZVOqqqsh8N8UIyr5hbFKGplwyUzUmBObqNCBd/QUTl3lKMXXs2xKwYjTGc7XIJ87aIt9gJw5qWmxymE+dxGNUyQ4s4izpXw/sS731+TRicAqg4EMpwLHqLdcvS3KMKtrubCWd8jtIrc9vGEk9Hk2oYWDptSIeYth7anKz4bKYhEJyn4uFneoLOujibNR82a+V0RetK5mxhySCpy2gQuYZRG0Y3rNUp2ycKbHNVFiWD+rZzlR/3EcXKONY99LB3i6VR4pi1MiyRetMuicoOCK0+78jBTOyTspng0UyMEg5a49h+qbZl+aM0+0mg8bVuXKvZVaJMShUOOdpw18/Kd9HzTl3kTMnajeNqmKcsnjpbojxVBsi5YNIdYiXcfi/SyD0sQ8kgDWDNNCmKrzmIPulseQueV/ZelWnrk6WcQOWlse2ct4HJf6nJ+LVdXYhuWAYTK8ylRlYQ7GSd6Q8rhAgXNK+hzpFzI6FWlLNythVpNQlJNh2lnRjZy3nnA6u30nPd6YRWds4sSiXyRh7uAxgqgJ08eoaXXQax+A0sH7/BasyZ10fCqa4wUwBVb2O9qSMqELPS15mzwxqNV2tIvEU6F1o5MLL2fczLnQieUCFeScR3+YAEUZXTAJedpOMH0OId4vMNBkNgV3bj58RuX8ECcQhBSlMQRxSEIc4xCFufEFyMQLfhcbQRjaCokHt1YP9+xeMKoT2muv0EOxSv/9IcMRQFJpYcyL0/lGgh8LQgTWn3ZHzT4sQ4zdV2ndQ4MyALOYhTUFrSU102gLSq+Ym767D14dquTr4Wz2gYqfngmBY1MVbrjwbqhI8hJDDAqReWtD4Ev2RCF4HRizlsW/hvP8GX42NCstRF3zmc2UAzuXog8AYRA/OPkP8S/9mdm0zfgp9ERtVAaV/FcDYFL0JfOfET0K9qAvvWXPmp/beD/KcBCEBlYEqS7zVgG3OEPiILffsXEHGPNJuC49PHZMMrPpLKeW9Rkdh+BK21JOnugltHMsAjtvqt0oVDK7itQGITPmM9URRMaVNo3onygmCGPOjz+KrHTsxQhA9S6ledK3qEDZmpWbrGHcaUye5FCvSFAWsKHfQ9adcWkKaC4bUVUpriJrwkrBdxSTPiGgWM8cmEDrqK08jUiozpyUjUPzKf6omBJprHmG73+TuRUIPrD8eGKh/AHyBETQAsIABrjAOHlt4Ab7Bb6AO9XRyMbh1LAli4G3NX8xRG2grDfTUclNLE7kIC5Kw+tVzNW8rx0aYo4E8oSvafeLcxzgzDuycSrN45kXyJLANVxK/FjDtTqYZbjWbHTbYBUUrYg7hWlMooyneRq3c5zNVQUXyhHv2UN6WaCoUbWzvRVbx44s5lUWXUPZjRQnhIrAZa1b+2MvLIyPP/JBllaK8A6EfR4DZmJBSfZAVOtCjcxk2z0SF7ftR0ll0x3k+Eo2uAnxCQN/NAz5Y8Vl2aRpKVt/Mtqn4m0V8LjK13yGK6goFoB2p8NZDi9TVP0qggngeyTg1MpTbYk4QrqgD2UB/Cs7Xiw/rdOn4wMnwAuYL8Fgd/NOT3ccb5UnkG/3btwPqf63tD4CgwysI7YWpNFAu10VPgdTY50HemRrK9nXgX47Pppn7MpC/OqnHVkhBClKQGr1G4std6B8hO3REaHsIQ98M+aGDIYlPZ/sM89CmTIMOdijNoAJtaEMF2pb4LBR59VIOLT/JawUkzYdj/gAzXtyzopoPa84yQlOqPQKlb0sqAunjVkz5MV8/qKnDnTg2W0/ZMqokJx7y1kX9kG8Uz1Sn9dB07BZ1NRI8rs6mIYx5iZZKntYCooNskOlSmqXnzOAYn3lhV1xEVVKDInP5e57ldMmnSTqFvUMRRNMQDxiQmOyBCqiZ5EUjsbqCutZsM1F1AIpQwhLF2QbRtCMBAmZBKSuyPRB8OmVAuAVCH/GDlPIL1n8O4VuLzjF6i3Xx33lgoHNDpXUrwhD4XkEAPR2JWX0HAHBNgAVksfuqOngYjidPBxsEntCEkIJKQf0XBZ4d1g1nh11qxbVTOMYp/FO8Bj+G/2EGgoLNY5qhONQXqiBX4AAtyEL2X0SWxXkZIjWw8ii1hdqC2pFBoUMpmWbwDLRGllv4AvqHi8rcPCYaCbr6noQg6Aln6O6BAaouuWp//L7XfCFOkHotoMHLCS3zebTuPNGMgoIdgWopjf0j7rLgQDb+TTyWoqafARnxRHfDH8AzVOOLC9ehugzqVGDUvM01r/zlQCwYUJbFdYhyPkTYike8A+HGjGWV16q34PXsMbrLMKDboTr/xGKLJe8z2BYgx+TXiszSS8UoxhMEXvpzAR75a71fbbvE2ZS5+sHdsmPwqxh7y9N9vGrk5/j/JCCMB0W+VV6oKTNC27Bam+73Fb0JyHwUCJcazNSzZN4IiA4mx93+5o8kQaztHwLnOe/NU/lF7IU4yi+817EBHb6j4USRRNZIqqLa4Iw+3QmlE3gtYS0VoYBMWLIEiAlYcQ8b8wnXo85SeAvepTYkG8AGHMynky0bbMKBVVDaxDC5hYOxnwh9jq6PLFjZNHTLfNXdGDRYbBHB/yDAT67fOikJFg3l2sF9GM+SEpoEsW6i6JTECzgLD/xy025zI7kOhDVcsBAnUUjixh2EjE+zwD0uLSEOsnIwKOi9XaniXOKJsTAI1MRS1WqGde3HAlthu2uxtHPw3SnO17YqUsm26nU8YvL2RukoFeCkJnilguf6XqawQJYPJakpDWzElZ7wWfKqal3yT7sXr7SklZUho5PfdIdTDMda0qByoDw+ysGqDFe2zCipkEQ8wVYOpbH1m6WHiTNKxuDfkQFDbhTiBxNkZrYIOMaF92Unnp6x/RxQJStS47Voi4SpaYB8jpSNp6ziaBG4GU31OLw7SGyjh35nfUucxZ4h0xuZVE5O3uESKF82j/db/TuRSIR6hHdai9xIlQo+Si2yiG/u/tJ+lJRdnAOpVmp5q3EWGWS1RcyUM5lRzTV+41tDQKQAzYci9i31eRdW9wbp1jAgLlvFJK7PfFR0xY6hoqEJNH23UI6pelzV2tyPNUcJsWyF1BjqPkc43j5wsbEzyLjYXBePJlSbQQQR9LblQQ2KUKNUA4hszPjRO8Y9YkZ7oeZdrS4hCyLnWiJrT9JqXAbIjpkx5EccuV9Lwu3YOcmMsQGZJ3myHzNruC/0O+/naCO2JHD5kM5lBRHD1L6CBsUAHTrJ8ip94nmv7AKbu5uOocpOpfTY84SeGeD+z9AfVLz9qqwJJ4XmBmg7AlJ4jmIUChSjUIDSBsmcDb5it5dDYMJd8W3PNODJ/ZMheqa5MQqdUxWITD5vZu/Zw2NZHlMNqnqSI2H7LYR1CvXS01UySbhKeX97LqbK6Ggl+HkNjawDTQtsgU2EpWwkRVHLKjatpGDiNForbEKlGApi9JTd82boSTMx6ZynEc5ROaVyOldOMhfMwzzMwwTMxzUPxVqitwBD5Gc4NqPc+DB6F9Oxrrq9D0+vo9/VcDOlhDXQKYQkNaNRAeEjn0lvbUtmRC30nczTcfO1GOc3+vF/iz7fEHoHKWje8uepHXn+hJepOnrWObyFlBXxcRaH8WYS/Y4BwZoYYAfKMG6q5+0rnsW7KD6LqTv0YNnd1ggxllKeEKbujhu4L1GHAoDPp63UJWnSOKn3dkjyAx5mCJFPGSIxTf5KVNJ9ncJYGdASLHKASL4NeGGOmFVVOpWHLM8qJ0+8nWZaTOAIQYQvYTLVZMxc+6RKK8YswWG9yUzEnZF2vsTh8Ayofsg0pSeY+muOjkTwdT4drN3xDIj+NCCZ1TCA1nwCSThQBzo/8dknQ4EwmZ9IMJ4J6Y42tOXm0zSk8RXy5SEfqBdmF4Ipa4bvLANuEZBwmnre5JXzK+pkcYDIRxSTt2MCY4zLsxXaiPzdyv92Jqx3t04H31Nfqr88gNodMmer7WR4+hXn6c6/D5dYwQL5D3V/Rl+o+Ql/6Evu3fuG3wW3IasTKprC1zX965XHnGFP2NaerkMZ9QOL4ICJ1XVI1NIA2I19bwn709nRJHfTsjCBCE6EhdymRHZ+Vao0wXtb8bDjuMNSfLBdEJnkmITeS4sszao/04fGn/kVVWMSfMp8OiD8eqBNWcKBcJCeDw0vg7YVR/UYAqAX3Mk2gfCCd9xX87C+rQDVnq5N/6pilL/QBOqeievSnViSbZXvJZSFcZQeAi4/R+KDowwi5CjaXkdScXvZpB5OBAvw/zsuaU7DbX9mXG/WoOHWZ/8XadDQMeX4YEmiGoURoqIA4vbWfwBU67QgSeZYN+uQhi6A93gC/jWGv69LQdJ1BMx7CGF1cyIJcGew/5RolGhEXA3J+Yhusz3ESST4JKSyNIhUsO7zhNVMnywGvsjF/Uq6ceNc/iuGXLevQ/oj++7op0LFZkRGuoxJ+dQ3SyJ4qMIy8/CXeg7qIdSzcJJQtSaxz9FD5N11Js1cV6sfebr7l7cOLu8qf8yGVxlQnZqRm1o1zixMkgINSzYAV116GE+3T4CzuOre+ODguANQdwAqhNuCNaBCd1OQ2SlFZr6yDywVvXw+jeS9vzqP+Q/5/7I7NP5FkCwsawJYIHk6FAVIAg13ZTaLpjPD21FXfB2ZKlEHS7hUnwJIwkJ9CaAKwvJQhn0e9H4xx6LikvYSLfPvawza3sCJOQBGUHaVlOoOOLEAy2KQ+EgrMTCHhQkhEKKobVAsgg7Spw/uHZXyYc4+Kr8daCQqRahgDkx/PcksNHPC7L3aCKVP3vcv1mIWh23RiQMyUZpjDNaHoofnFUPNnoO7cA4eRTPCxvFmJ3gtQ/1cRLnwHEhXYx9q2iN+f/ce1LVDrTYVZMHeARgCJs7L4IYQQe8jTZ1CU8SbiisCy4Xmho7IaNiYLA+G6yViJLFmeg7rwbslRqBFHbQsCLzgMLhju2p9BDDCUX0abkcBpSKFEij44AdQfla2BRGScuwzSMPUkHY3Mw77/7OEW4Y5PLZldipPkPjYRL0ER1jowKBqzIycQE0yYp/Uqr41X2nlKnlo3Owr0oxgzEiMUk8d1AF9V/wviHuEPuEgyF9k1slzp8wgQ19zNAnCZt8PzxseIjiz/ZVtkYpGGEbyak5r+lHyhUCH8N+gmJVJEgfFGRjnwaA7pQetbAnPucrEitlDZGY5ZnxnIH1sErECTWX1x6dYKSYcE54Rx7114zmYDYtBze7UZ0vKzPOfOaY2BRRSZ+iMhSvhu/N9pdgcLEEPbsIj6INJOHku+DhimFTNEk27VGoXggziFmkqiueE5FBq5AHW/AX3KzdoulEeTBwemlYRoj1mlYdfx3aaxflUe1gJTlfCdg7peIOmRH6YiheQmXOJ6tIwZpCrLfUAXMJPoXyYwXUkvcfCSRfbwIgjzHwUTOIHWTOD9RyHQFY/NGKUCycVmPvUMkEH2sLleCFGrrJE6zQiVi6fcBzbKpBiWS/1uGQU53aLPSqHiszKKBWcb2M0FnTjKF6OCZbnuEiZP51OVv20oGkSH2Vp6p3B9RbFp9awZr0zcgI6KJ30eOr535Isl4uTDysxG3Mmq9XUG+a5rSwerQ4WlhmjnYGb5hskN0vnvNtnSFN1zd/Kv63tnGJkVbVM1/P8qG5XIWR7RJq/S8SQe4bFLGjsadyk10WaWr6fPMhEP9XERCXPlLhRN6uuyCt3rq/kyM/jy86gQ48y1KUKEatdMNoQyZpEl4XaVcyZD/cLDUfXVbb0Ytp7B3sNiG6VOYcJtIe2lbyKeWZNnMtQjnEdf0+kPIMGsKRpFktiUHEvjlUG15mdFcVWZBQNGCy52CXidpwHH4EKxALUdAfNJ9Cva28Sh0cCzF0GayUV+j3kD/8PHRtUPdEULaZjf16/uilTXuZVBZqkMV8ZxDLPytX2c3ogDtw4EDrUw2aYeXuv8Mh18Npj+fkvVLKmD+MuGGAslue5i2MuEfuv/RDP0/p0q0OoRqnUoIwi/Dr557WDbT5Zd+NHTQ6Tp87MGdaPfQq8r/VbXr34CsSrwTOsmBwR5W3f+c/0tsGYnJyBU2GVTpfwa/G21snDOPQku5Kp7L0QHafK3VPtMRBNY9BOc/GQKRIVSvLFarWYH9hwFQMVFSujm+IOgMctOM1GowlNcxyvZvNIP9FlPoEsQfja9AG6QOD+2u+m0RrMjS7FYq02kEEoZh0Kcmaf7x+k0pULgq1ldotOZ7F3tVWVCBZWMUZfF5OMoZE/HLdeZsk+VZWZwiN5nqWwoumqhryRnr2tKSMQOE4hPvXOcjhgxcXPybcSq9CQ4AsRdyUXs2SyZj7aM17qivkn3PByfY/bCgIwj0Z0CP/02X4GYVST6NPF9zyanOgXzuFsDN5YZPAdrMaXsygty5O0dW4qZeqN+1yFcVP06Kyn/Hxvq7axrKoaeQYluKSmK3mZIO0s9TsJVu5wPTbxiuJrh8RDjLIfpELs+I1ko5cZlNUJ0aG64I8vA/5I9TLvaVE1SSwswlI3965s4gxPLVwP+VFU6xIuC26UAoafCGjJC99AXqZzFwJBUIQCFLAAIcUZhCcpKAXmbvAQEQyce3QxuxW1qf3PVv7MSxvgyZc457VItQsQ/v+Tf1xupDWrTwpPvs881Hfef9TtgzSD57b4xQsHyNETY5113AcixOcsvhPBtx+7+gJnyEN+e/DbS1YQKQ1X4tv+ZAGksPGifd8tcmPas0LiK8c/WMa9q6+savKBnc/1he576kupgnu+xDAdihDr1FnGIiFiDGeKtzhj+aVY2l+AU6hCE5rYoUSlTKVEpUz1fR29oN62IXqjYUBCLC3Tqbm6ez28e3xqw3yeoQwGyLAFjGhteTh9MmrDQ/hBjJzhOVUUK72sXA0P4KuR6LhQAioeiDGKRtv9UEu/ZH32AoBxmjAhkOS32CI3n7E9dw4YDTVoQAPbkIMG5G5nGFHGvk+sV5y1UERbZoAreOAZkTK9wdzaxELm4xUXGmpEGfv21+VKAbQ/wxbEF3UazYgy9l2+XQaA4at/JBCQgokXEWozsuNOACOgCAUoYhGyUIQsFENfjtVfyzDcEVUg9/OA1kFPmav/MbBCbzlYdaa4WggduJrhNlg8vRf7fTwWIiQc6kkqkW/fFLMa1Fg/Cu8dsJcc05lLJCjxCMdjERCUXZh27QrUDWKECWIBI+VfLHp78o81+KFBbaSNWqQt0sWoCqbVl6OMH1G/0XZUdspOn2hZ1AOkNiSSt1LeCW14hIERzNw+mZCAKQcCsBQeWTSwDY3Z8fMxU5JSks4lEcdAJCjVswDYGJDLE/SC0It+xhCiSCc8nGyqiuq/1KEATjXVGPYHlHB6+Hs6lWSvb1OK2aYXUgCPbxL/C4wI/PB3tkKu4QoqMtiHfdyHq7BDtQyuQ/eESCnXnpmLOa45yPXywSTueQs1SdEnnRFErwH6g0CVSM9qmftDh7OS7Ee2SdAVp6xtiFAZ4HCfLXqErJD8qt2Mkole/BkASSICvu9NjZjUGoJ2TMaxUtfV1E4RDD4Dr9ekkqEyvAuuKlGJ974ZlcOPDzyFgpTVyZ5+vA4kbnF1fY+YhOnwTBi0s6g8xJUubeNRMdV3FOOJcDxORJJUOq1pH7KOuSzM9k2lMmOjH1rhaeCLeTpUjJuDQgoMupoAcaYAUAps17zr3Qvbc7vEDZfph6E+eyRB05EpcYDE8jzc6el7x4TgHhChjsXbNGuyOWRly8V60GaGeGItDyuKbJWRrvgWfa3+rFQecVzaLXHCMhxHyQzD8oLAaqL2MXKrg0VWTOaUFM5qeBtZLmoQckrn2R1E9w+WqIbsdVDnWKiiAh2QQEs8FMGTKF/ne5u0YQX24Sps1kfwcF9BhHgkhE225yYarlJriLNcJFoNp7eHa/sALdBdn7BJTAIsZ8x0z5RVQX2hwIKjE1PC+EZ9JvADpURLZMIhEA5NgMVpNbaqEDXoSSkQZ8MgI07LUhFodEqtmAolZ3oGKoVnkUyOcqSc+I/EUhuX9uRJWUns9pODbPEkIDMsf5bIZ+4RW03MybLca8UYw04/XiqVONfgiU+DS80wyE7wPfBmENYn8g3kqbp0WFyR0/uw2YQ2Ap8zqy7xek0t7FUY7jd3fjR22CfuHO2UcjNo+nhaJ7P5tNr6jhwUm9tZwxKkWm6KZPZM52THN5n3jK4sOsOX6TaitrAV+a6drmOEAtPxhddkHcfTFp57qbsxUyu8q9mjH32GLRJrwYevG+G+nEUtVdjqiWG/9zYLzkEBGMGVmzHwu6mvfB/+EP4B/gn/FX6B2hF+gdqR+tb3wbcOcPcC4tKsnoZi6tuwMsOOdQY5KFizIPCrtVNJ0/29XdfPZ+PCSwlOO+ed5adTujB1e+iF9ququufG4leWjLEs9vygKNW/Sd45Xx6mVFhFryJaahEXzGC3+VlUZTCo5i/gjRiDiMR2xAiLWlDR1EQ9Pov5RZGabSWEXuC7Yc57/OPlM87PT1gMdc0eo2w0sZeAkkxgoSz5S+B0W57XJXNzIbj3B8VeSOltuEEKyRhidXIYnOUNhuMOeXffIEQrHpWddGZE0eV+i0BiurhKOF/d6CuJF3WuFuZ6uGv4C9GQ1tYClURYlRi4vhflK0XkhelQZrdKyPXrHpx6skBgQUr/Mq/SCNzW1BZq4aJm7xnOyrN9JRhGUM5XMtsq5xuXGpwWzCPO0z4M2IaPgzCR2PAH6tV2V7R7Lle91RWhQ1Yq5bYgClduKRSjsWaSAHP1Vq483sX8Wdhzq/IZ6uYdRnnPR3VYZhFabHeVwxkg7rPcR4CXDzml6o3iJt9TEkEa8t4LRugCSV+kJH+o37TYMMW52ysqi3OX04FH/uQ4mx+ZN6KFVtafoFzE1k1ZnqGCpoiIbQPSJ02pBQMZmcKonMWzVnl8ql1BSo5yhmEPpvJJrInHeHrNnHcF2EIHln1g1sIjuZgKCpU87hUBFckxtz3AwSTw5csjxlHvnRUt4lMjrrsxROlGss2OtKZMLLbhWm8YGWBgR1zIe1szl/a1kRrOBd2KTCZmsF69IGbl2llBGtciGxKXPuvtvjuZtGiQY9oNh6MfWea+QA3Tm58hReOgZPoCC3hLUPBgRPVlnxPTYyLnHhM3kWlzGzHiiKeOzDnwzRQWx/JzMzxv5nbmj4ogfd4PXCC/cQLN5FwnBv7rP5QBQx77Ugib5T27JNvFylZl+DPScVfM5TfXXFEIa91jmBgTwthBOjsn0+RhKRNztVRcRqbFsDqIMixvZyVsDLyJERHjZSmqQOmm2QVKlSC5n8H8GTbMmcOsdy4cr7QpYmKHc7MSB1EOVY8RKxY9nMRlWoyrgyij9k0rYWOxc3fwqT/tCb+hUTvaLdUn1SnVLxmACgq7qU5hL9Xlhj63kLSKq1zEC7XxsMWOi4OBiqIaveHjNrJPUTBLaEK/ao2O9ln++kg8SYQsND5SuUjinVSCMFMpLZ/k49oMKnMSISJHUV3WnApRIU2JxpuRhRjBZelwbWqq3ErTSVW3NOXzxPaJyvmGp3Hc+FQsQPOreLq8/3DtvX3LH9HwxdbsWgfeHgOqtcpijl70wlOP4hANuzFdNaelcROwAkGWEOGg4lRJyz3lIE3jUWxTBdCkknbAxKNbgC44GXmVhcH1kbrkTsTSBRaR2FRBEIcsvtiearIwAm2Jq1pdquw5Ebus1CdhZh6Oj1myUlz1XT7u5rlUW3zw/EAN1TIKD+ttktvrSkhmpFn9mZBa4hiGF2y4E0cK+2yC27LDnLJsahAXGiN9qys4Nikxh7nM7ywxvfojKv3HF2o6QaQ0s2OJ40ivfkTEZqbbMzrsZnTnBku6X5pfzAjTIH2sD6czghnLkmp6UmaCtHij+Qx7ZEqtzENUy0j58/TEGw12uHOmSR1ia94emDNAgkmdn9yRzIl1UpjhhajkpQTn5ilFEIGpAvbewoEiEXbWhWpbplvmxQ4DvWrUlrnZrrgb9l5kQA2qrGS4Oj6IHJXXSiBkgxTzM2R7RF9Oj7sbqrIqpufc4tLaqnejvxGG/Q8w4yUh5IP70ZDdnO8xLVX9vb2NUmaPEb15MMVceJQQ0UG3HKfDOAMxmAppcUoCGMSqXg/sYMciDdFs2HkrWwNwOerfwllFVZNM76V1ut+nUJklvBEvSvGVAwoOjgsAv6pDbUqNgKs0p8ZNI2VEr1xxLpvFw9NwIbK4t91CtdKV1dgy+Td/sXjhsDx/La7Vy6xsFmVKMGn72eLRLGPyrocI5/KkSiOp1mH3dODWzFJR02O2SY2mcwrcLi5imqebGV37/bm4PdPSh+76bUJOt8CU79KQcf5YlJNmqJfkQiVW8RkclKucExN1QwTiy/DRTR3iUKi34QhNLD+Cy/VY1a2a8ZnVW9hm5PuJSprKptdrhZLDvDWk1pZIJZbjBdqiPjq6vP5UFBhO2D1W1yPit8f75UMSmsfQLN5RJTC4Dzus/+tArpiotRLNbjgpYfusf/1y1zl+dxzqRIOb1InUheQC6kCqatShcJfKGZi8CUCzwCmUj0dHGjeN1nCY/d87O9Ea+jZrs/27XUe9HYZTfn+rysKwT+2gXv7bYXiPxLLVt21PgGU+898ON4Rhy9XtNO5NJHPgygjHHLeOzH/+3jNA1bJ3afZ/t9c4a7g2+7/srz7Zf1JF/6v7GXAGyo801o5WEWb5985OPMa77cwGlRhKUIQSFCGzbcYE2n1sXKu1rGpYJzPdtlmA+HfPPpjkg1sP36gZfrKzNu8ZbzHNIow/RvDofxGj6umOak0ETeYhwTvHmYIqdiTeM2ehnTmlGHBs+xMJAa6x1AqKrulket/A/v8/T75Yzbuvz7swcr8sApIfqIoB6Qzo9ugx7lpUQSldoQtd7FKGUimdj/GmS5SuUC/ckNKX6zz1BB4y85Cbcb83jUOk2c2Ll3VkAcnhPbC2UANt6ypv7zLV/I893L3fGk0J7NqH8brgnEXON1Lmj5QTT8rxkaLvO88pIyeQR3F43Wm+z6C6trQ+Us2WiHLFylxVIqVeALCbRxJNxTxjQ3MHQY4gjs72vBbhFdQ35To07fKY45EMwaDN9pkzELSdkPVPZB4xU3JR/60iM81uOAFRVg64fnn/eHhcrIGC6fnmB9c+IIydgJ7SzW3ZpTPtrWkxNjjF+qf0MoR5LJ6C14Nm7wB3Z7mBqLRsLom3yH9eg1XBFiWX03mlS1x+ulnLwrzZr/CqygOJjrRjsrM1HyHPv1D7H2lil8Cp7/Bfxg9KRn4OHNIelOywl+8HgXO+wLERANBbZe6kG8sULefQ76c53M5Xo80+WnyiPgO/2DjW4DPc3G0cBkMH8B1VO6bZ5alnsOXZuOLHk0I/IcXMA8v/exKCVz+5M0FuTohY9H38s3H4r2/4BXDkdjoYkC2ilR01CsZ1XpRUxWTUl/w4OFmYVxTXCUCcoV+/woYiaE+4us+yMs0xoAMd7ECWgkbxCrUT1HPEYeQFCX9BX3GfUXGTgHOjvyRomqDUXxFIlvsemakYICyxgdO+fec3Z0r//v/i/1nrBiO1dpski9DamODRv00eEFV+djv5UBPy5vxXaxUTp3QWQF6zGEzJxZjoJMvUnsROoq1JtgXs1EZ7Z7mXkJFOs9rFZwwDZiThIHzyWrBzAZpTwDRMbys9EBqqNK8IEYf7d1W7srPyBDqnFqiURkiDrV3HDNsT0Fi1/2lec/Ore1dnxn8gpUeKmVqnagRZlG302OdT8W+v/5kA1W8zKXu1JVPr1PwUKY+zitDijYmkAfj/WkLHDl+E9pMGaNogZNLyOauYcttfk61pmmlNjfPgDDptZu9cSdWN1FfXewAz0KvPAFwGy4n4mU4TidhC0gAKQKWhF1ixY9xAq4ZhUBs12MNlemOoF3HEArintpo5kcxtjhVjtNk8j5fsYE+mX7GlMRR5QSYvhiWPB2sHDue8IdYBpE7rKIwa83F7Q8J2N64m8rysqP/ffoQCUsUqpOd8Ori8Qsi5JpY1K8ZqWLaKDvokJKPRe2rKnOcniXRaj0Z/VmNnQZ7BU+9Wquh8TMVFHYxKb8e1cND/MJ1N0OQ6BR2IiyEYqRWWaABQ0Wc0tgfLhgvQZPgT0tUo4N8A6jkAAM0atkA3O6/v0aBgFCYxGNUcMzRFxzyniFwsOJ7120DJUl21xOwzHm3LHIgCFajcTolP8r9lQ4A+g7EcLL0aDXatxyfY96s9bcLCz3UTm14ExTpFmPJoxAEi/tamlzqdh/ipQenW6ez7J8NVZJp/2JBg29+CRoNqxVr4v60vxxcyGVkE37lBuTPOGRhjpDxZ9xkzOOBd+HTMrNrGgRwkR3CpDayKGkqqhduRQguuFmJkBvKSqOOoH32f/KJAdaJMoxpGmQYhgPmIAML48QD04WZzFv/zF9uPvGE1KPqUYeIKa7kiAy6Scb1uN+MdclzPdS1Td3CILV3RIOUGstYw9lTw6XYN5q3ze6IpW8PU54USSCu99d5yDydshXdxFBGXO9lgg4RosLhiZOY3dn7S22VDNDS25yM2HFtjQ2yJZRP88txJ0dhYdwxGb/t7v3uyFgXXarurWt7M6LZS/bD+NqucZ+SSbdh4sbvFzqwxQQQeYVtPEv2FZx4rW8GgK5nLJYVD7aTth84/6igFMzESE3P6b+Of4X0TNuNb+Dm+ja/+iv8viTGC0lz4K76GF9X4aZb+X2i3L+xuujOkese0bTPJok2/n7CnePoUXG43stT3uS4SJl+fDXQ587tmreuyPYA+PpH50+gX6WaTHvMAhwt3DskYsZKOaDqOzHIQPcbr40yq9lNQqy/kX1X2mkBE+I9GNN6S9I/9duQTk2dyt3WM9Rmz/BjGAJ8b68JMkZuuvqLi3fGizSUxvh48+q6+R33Lzb+/b24I9Nstm+hwcLiFpocBNsZqlXrxCv128qZ88vPGBfwjIIPyjB/wsym6n1RD6lH4iIHWhsoaeiEkARtsf+i7Mb10R5bsWRX9Uvsa6RyUrXPYbLHR3fgjjeu4KnR16DcEGy+M7iwphH8ajms8eWZhSsfX6Jk6rLE6Nl6U22+1SfIbM87pre/b9vABxm0Zngc/85T3i+vkUtz6sTIkJ54Q5gsAgza0sEURShFaR5zt9wshAOQrznfNB6yM9vtwoU9hm+XsqoRFe1vSCgP8ANkZYPUBv5ixVt3Xn3zgnedffJpV4oU7/jD9OwiYfbF56Ux69cRwwySTdf1BcLysqjeUj+0IYTbIyujJEAEFe7Gr1OQzdVOcDYgrcGAv/GWnRG5lH+pswL4ta9Gxw/+8e9/5gVj2p9j0le3wdZV72e1DC1rQajuyA4eLA002Q9NzdHxI8FQvujha2fgF/L2Nk7f1uXYYk4//OuZv35MyQesO3M/ShG0YcbQNRvI/5G/fQB8uPnoSezpNkt7t0Iq4QQ1y1K1j1t6kJ5Csz/wGhev/V0htfE3ZwD7GXy5H2Kgwb76Rvf6Xnfq9Ui31G1ev/d/NENBLOQX+txC9Lbr9AdW8VkOjbyp5u1hJLeF+/uY9aZl87FfdtbEH57F1eNf7ulXmI8VHf5j9j1npa70efew5oQ+/2ESM12ff+fysr4LXprq7OQGFjeNg2ASlkm5k6y0FQtcK9zcPQxurf5HO49E+vtJ8ItI3OqPtb8HmKZjnjQ1mvvd6/GidfL/4lJOvi/YjkCTIoeJxUjaO9Kte2ISqvtU/mpZoWaa1RWlhwWf48WJVEatnYzncNfL3C/C7N373vbddSSGPPvGToy9cAwS+sw7v+MC45vXyjz1FW53jtzP/5enPfMY9v26a/Hbzu6W3WY1c/d1d/Db2Z591sOOl1mfJkY3NsSXzTXyN/BuiwQJSM3+rTpiZ7xxV50QWTo/WcPVY7PYaoj9w3TnA9FdT5AqbgQFNiq9fe1qo9So+u3l7wO/d8B06Ffg0DK6y8aZSnGQOtMVzD/76xbdfJ4zFzcWTT1xsx6PXjz27Fs+DTzIX/+mln3XnqU+A4o2tLbx87cVu/trOXz69HPsiJdU3NmMQ8B4b+LpFQrsZDojtVX7fsra52dAixJNqPKFtLK5Tf1+KnkxrF8/Ss2SeXw/Wmb2eQp7Jj+cwPzjvdgoN6neJPc4d3VrM8n763ySMvrD83yy2CaVt233V0u4K6NlUFduaOCnmbEQ0aLx7dDtcC4bvp02D4kyM4xeH94gdmAbO5XTidR3vj03OezD0E/ZcHG9tC6ow3eEpkAhS85feD1bjgRdWSpsAYVm56nlkzvoe01HWDxFsLr2b2rJPLdvXxKMnpjgAPjN+c38q7g/LH8oxN7K8M1oczios+59MvQZ2ZWndwMXvzGNd99D/rZPBYM/Dp0abLJ69o1wkjt3wJ4X+u/sDr/f+csqmlndGHlXrLszMNVW54FPTpmabxx4C/Ccr3o2Ic7zbzeCVpb0CueWdXzKnNEn+4LEVr5HP378G/Q+3Wm718i6Rsb8ynOD98Z9/ZRCakLquHDS4G+Go7xeS895waJL9nLpOtvz0536h/84bSb5L3FHONnFv59tMZ8mpv5C+GwwJj+fOaOmaHX4T2DybipuBxsNToP796B9XblfAxT5FP7vjpT99JC1sxrqPusVlBAWXagn4I1Zweuqk0+BWTMvGZ4vDzzfAsqMOz/7b3VSAWdTVtSJbet6WdgL9n8oN/XqPvKs2KR6siu3UT9L0hwr9V6ehpM35du73bl0cT4P18QcA0zAXmq/27Xi8DWtm+DN37VSkWtusrRq2nWm435W+wwWWXdEcea3vc6Pg9bXy++MVAU+oi/v04WtiY4XaWvQs29e71Uu3Fq1k/LE9yedFd1zJ0zQt5kzC2ff7zJKdYHs0duIZxj3adjbDG1Eee3KPPTcr17S4OrpJ4nwbN0TJ5Us7q6YkUXv65qK7YV3rVi66vrTJL6VTFrXD9ebIa3yfG/qvr5XeH68oF1X9YXCPDh6iw9W4Fo7nzSd5tO1BpoXiDpODTgePt9BvaL9wW6cIhTZE2D7yVh69VTFIX6PUBIRUK4OWTIX7aYzNrssS5m2wtLOB8n2l+RoVQGoRVmrnULdo6EmRrrqTm0cwGxGUhRkkzZyHZiDSQCQoQnEJchF8voqfkCUTIS871LGTokZpFEKQ99lIJPtwZ6wJh58M5PAfymzvSuAWFuW2QiHaS8GXqax7L70B9K0hv0qBohDrm4yOFf5QshpjXqBWfpVDk+gdBr5/Y3yhBtnhB9+i5bsXP4dqoT2hdSFnjvxlfAHEHw4XeY4Cab6w+thmvOzNCRPPAIBHXhM6zwM/DcL6dv5zaRqX8G7SYAwT8A8ccuSMBP3xLEKAjea2FZ/pfupacXr/g6HuM8RfeJNx80aPE7+U5BYKtfpYMprDj1/E63H9OfZcXSNWeajrHB/qIXLjDSgtQ65WahnoXzKGtR1WNU7biASt0iStSjRVB/M7lvEY8ZfFN/r6ERlVSBrI6OisJuPGiWexTrbEV+cO2rLAjVVDS0B+pKAiP0IoHblcQ6pUZGuSqzHPwHgU1EJdD2HrUVtB206SZqKmZRCbrPEWJUZ5eJOZUUs9+vLkcXLeK3oOyqAd3dcnFsBllnQIV0/4BVpVNYnEp0pEo7tB2inzDaZHpGW2U/NJ0Ux8rAfIqCcd6Ek/di8pT/OZ3iGxsGyWp0MOcVURMZos29TbCNG/YHyEh/sl8l6qQyShA/IZ+aZ8atOPNv3g0iPbp2iI0D5KUSTg2Ig5f/2jf8dal+etwiF78rCKhPaZ/NhGfN8GCVIcl6tivDwqrblrNWSErNUZI4GDmcBw5zOJYsIzhZEpulajX2pvRSby2ZJZcGlhZsOktZmDMM9kLmw6+S4v51FK9DZdznwEuZoFMOmFLIlU/spSsFlnaWgOshAmZz3DTbWS3VSU3KYOyW8amlUgrCKrhMtf/PtVsPm7kAcIHdAJQ9ANTdAAjdALBOAAC9jAe9zlpC6iYNFJJ1RDO/TQ7dXYAc0xa95GNfQdb/Ts9pGSJEzvvKIEmLtpgCZvqAyDwICayTZHON6hw1aog/rl7bn3RF6bvJtuvT81+w3F0Am94Mib3jenq6EbeMAA1r4hD6SAgS1XSqPmn98v/c2kfT/C5p7LiLGpoPcSMgVKvdHw1jQGgBW7+EO4H7U8O212bIKa5a20DD6cupdzNbE65DJsBy0MXt7NQ2b3qy/dUvbaOeyDNnCatFFT0uHa84XoVzuthDM10FKYVb48V1HNOe7ggmA7D1ivSbIQ7YbHCBgROmpspJfCYH/wvUVwYO4/vrwi5F7P5vDdAAAA');font-weight:100 1000}@font-face{font-family:'DM Serif Display';src:url('data:font/woff2;base64,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')} :root{font-family:'DM Sans',system-ui,sans-serif;color:#252923;background:#f6f5f1;color-scheme:light;font-synthesis:none}*{box-sizing:border-box}body{margin:0}#planner{max-width:1160px;margin:auto;padding:40px 34px 50px;background:#fffdfa;min-height:100vh}h1,h2,h3,p{margin:0}h1{font:400 clamp(32px,5vw,44px)/1.15 'DM Serif Display',Georgia,serif;margin-bottom:28px}h2{font-size:20px;font-weight:600}h3{font-size:17px;line-height:1.4}p{line-height:1.5}button,input,select,textarea{font:inherit;color:inherit;border:1px solid #d7dbd4;background:white;border-radius:8px;min-height:42px;padding:8px 11px;max-width:100%}button{cursor:pointer}button:hover{background:#f0f3ef}button:disabled{opacity:.48;cursor:not-allowed}button:focus-visible,a:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:3px solid #86a8ce;outline-offset:2px}.primary{background:#315e8e;border-color:#315e8e;color:white}.primary:hover{background:#274e77}.secondary{background:transparent}.danger{color:#a13c32}.small{font-size:13px;line-height:1.5}.muted{color:#626a61}a{color:#315e8e;text-underline-offset:3px}label{font-size:14px;display:grid;gap:5px;min-width:0}textarea{width:100%;min-height:90px;resize:vertical}input[type=number]{width:100%}.row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.between{justify-content:space-between}.fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;margin:16px 0}.fields .full{grid-column:1/-1}.week-bar{display:flex;align-items:center;justify-content:space-between;gap:18px;flex-wrap:wrap}.week-bar h2{font-size:18px}.week-picker{display:flex;gap:7px}.week-picker input{width:164px}.tabs{display:flex;flex-wrap:wrap;gap:5px;margin:26px 0 20px;border-bottom:1px solid #e0e2dc}.tabs button{border:0;border-radius:8px 8px 0 0;background:transparent;padding:11px 16px;white-space:nowrap}.tabs button[aria-selected=true]{background:#315e8e;color:white}#message:empty{display:none}#message{font-size:14px;color:#315e8e;padding:0 0 14px}.notice{border-left:3px solid #bd6d2c;background:#fff3e6;padding:12px 15px;font-size:14px;margin:12px 0}.panel{padding:20px;background:#f1f0ea;border-radius:12px;margin:16px 0}.section-bar{display:flex;justify-content:space-between;align-items:center;gap:12px;margin:10px 0 22px;flex-wrap:wrap}.section-bar p{font-size:14px;color:#646c63}.day{padding:0 0 24px;margin:0 0 20px;border-bottom:1px solid #e3e3dc}.day-title{font-size:16px;margin-bottom:18px}.meal-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}.meal{--accent:#a87b26;--surface:#fff9ec;position:relative;background:var(--surface);border:1px solid #e5ddc9;border-top:3px solid var(--accent);padding:24px 16px 15px;border-radius:10px;display:flex;flex-direction:column;gap:10px;min-width:0}.meal.lunch{--accent:#69856d;--surface:#f2f7f0;border-color:#d9e3d4;border-top-color:var(--accent)}.meal.dinner{--accent:#5c80a5;--surface:#eef4fa;border-color:#d6e1eb;border-top-color:var(--accent)}.meal.accepted{box-shadow:inset 0 0 0 1px #84a890}.meal.rejected{background:#f6f3f1;border-color:#dbd6d0}.meal.rejected h3{color:#73776f}.meal-kind{font-size:12px;text-transform:uppercase;letter-spacing:.09em;font-weight:700;color:var(--accent)}.ribbon{position:absolute;top:-10px;right:12px;background:#b4493d;color:white;font-size:11px;font-weight:600;padding:4px 8px;border-radius:3px 3px 4px 4px;letter-spacing:.01em}.ribbon.accepted{background:#3f7152}.ribbon.rejected{background:#76695f}.approval-buttons{display:flex;gap:5px}.approval-buttons button{min-height:34px;min-width:36px;padding:5px 7px;background:#ffffffb8;font-size:15px}.approval-buttons button[aria-pressed=true]{background:#315e8e;color:white;border-color:#315e8e}.meal h3{min-height:48px}.meal .meta{font-size:13px;color:#60685f}.cook-button{background:transparent;border:0;padding:0;text-align:left;min-height:30px;font-size:14px;width:max-content}.meal-actions{display:flex;gap:7px;flex-wrap:wrap;margin-top:auto}.meal-actions button{font-size:13px;min-height:38px;padding:7px 9px}.repeat.active{background:#e4ebdc;border-color:#8eac80}.cooked{color:#3f7152;font-size:13px}.badge{padding:3px 8px;border-radius:12px;background:#ebece5;font-size:12px}.recipe-list{padding-left:21px;line-height:1.65;font-size:15px}.recipe-list li{margin:6px 0}.recipe-content{border-top:1px solid #d8dbd1;padding-top:18px;margin-top:20px}.recipe-columns{display:grid;grid-template-columns:1fr 1.7fr;gap:24px}.shop-heading{display:flex;gap:12px;justify-content:space-between;align-items:center;flex-wrap:wrap;margin-bottom:12px}.cart-tools{display:flex;gap:8px;flex-wrap:wrap}.cart-note{font-size:12px;color:#687064;margin:4px 0 14px}.shop-group{font-size:13px;letter-spacing:.04em;text-transform:uppercase;padding:13px 0 7px;color:#66705f}.shop-item{border-bottom:1px solid #e6e6df;padding:9px 0}.shop-line{display:grid;grid-template-columns:32px minmax(0,1fr) auto;gap:10px;align-items:center}.check-button{width:27px;height:27px;min-height:27px;padding:0;border:1.5px solid #8c9787;border-radius:50%;font-size:17px;background:white}.check-button.checked{background:#3f7152;color:white;border-color:#3f7152}.shop-name{font-size:14px;font-weight:550}.shop-qty{font-size:13px;color:#62695f}.pantry-shortcut{font-size:12px;min-height:32px;padding:5px 8px;white-space:nowrap}.shop-detail{margin:6px 0 0 42px;font-size:12px;color:#687062}.shop-detail summary{cursor:pointer;min-height:28px;align-content:center}.breakdown{display:flex;justify-content:space-between;gap:10px;padding:3px 0;font-size:12px}.mini-actions{display:flex;gap:7px;flex-wrap:wrap;padding:9px 0}.mini-actions button{font-size:13px;min-height:36px}.quick-pantry{padding:12px;background:#f1f4ec;border-radius:8px;margin:9px 0 0 42px}.quick-pantry p{font-size:13px}.pantry-intro{margin:10px 0 18px}.pantry-entry{display:grid;grid-template-columns:minmax(150px,2fr) 1fr 1fr auto;gap:10px;align-items:end;margin:14px 0}.pantry-table{width:100%;border-collapse:collapse;margin:16px 0}.pantry-table th{font-size:12px;color:#626c5d;text-align:left;font-weight:500;padding:8px;border-bottom:1px solid #d6ddcf}.pantry-table td{padding:12px 8px;border-bottom:1px solid #e5e7e0;font-size:14px}.pantry-table td:first-child{font-weight:550}.pantry-table button{min-height:34px;padding:6px 9px;font-size:13px}.stock-status{font-size:12px;margin-top:4px;font-weight:400;color:#617057}.stock-status.low{color:#a44334}.review-item{display:grid;grid-template-columns:minmax(130px,1.8fr) 1fr 1fr auto;gap:8px;align-items:end;padding:10px 0;border-bottom:1px solid #d9ddcf}.review-item input{width:100%}.review-item .original{grid-column:1/-1;font-size:12px;color:#66705f}.settings-section{padding:0 0 22px;margin:0 0 24px;border-bottom:1px solid #e0e2da}.settings-section h3{font-size:18px}.settings-section:last-child{border:0}.backup{background:#f2f0e8;padding:17px 20px;border-radius:10px;margin-top:16px}.backup summary{cursor:pointer;font-weight:600;min-height:30px}.backup p{margin:8px 0;font-size:14px}#copy-text{height:180px;margin-top:12px;font-size:13px}.empty{padding:24px 0;color:#66705f}.space-top{margin-top:14px}[hidden]{display:none!important}.table-wrap{overflow-x:auto}.invalid{color:#a13c32}.disclosure{font-size:12px;color:#73786e;margin-top:18px}.disclosure summary{cursor:pointer;min-height:32px}@media print{nav,button,input,#settings,#pantry,#review,#editor{display:none!important}#planner{max-width:none;padding:0}.meal{break-inside:avoid}} /* Compact controls and layouts that stay usable on phones. */ button,input,select,textarea{touch-action:manipulation} .cook-picker{display:flex;align-items:center;gap:4px;font-size:14px;min-width:0} .cook-picker select{flex:1;min-width:0;max-width:140px;font-size:14px;padding:6px 5px;min-height:40px;background:#ffffffb8} .meal h3{overflow-wrap:anywhere} .meal .row.between{align-items:center} @media(max-width:900px){ #planner{padding:28px 20px}.meal-grid{gap:10px}.meal{padding-inline:12px}.meal h3{font-size:16px}.meal-actions{gap:5px}.meal-actions button{padding-inline:7px} } @media(max-width:680px){ #planner{padding:22px 12px 36px;width:100%}h1{font-size:32px;margin-bottom:18px}h2{font-size:18px} input,select,textarea{font-size:16px;min-width:0}button,input,select{min-height:44px}button{touch-action:manipulation} .week-bar{display:block}.week-bar h2{font-size:16px}.week-picker{margin-top:12px;width:100%;gap:8px}.week-picker button{flex:0 0 44px}.week-picker input{width:0;min-width:0;flex:1;text-align:center} .tabs{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:3px;margin:20px 0 16px;padding-bottom:5px}.tabs button{padding:8px 3px;font-size:12px;line-height:1.3;white-space:normal;border-radius:7px;min-height:48px} .section-bar{gap:8px;margin:8px 0 18px}.section-bar p{font-size:12px}.section-bar .row{gap:6px}.section-bar button{font-size:13px;padding:8px 10px} .day{padding-bottom:18px;margin-bottom:16px}.day-title{font-size:15px;margin-bottom:17px}.meal-grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;align-items:stretch} .meal{padding:23px 7px 9px;border-radius:8px;gap:8px}.meal>.row.between{display:flex;flex-direction:column;align-items:stretch;gap:8px}.meal-kind{font-size:10px;letter-spacing:.03em} .ribbon{font-size:10px;line-height:1.2;right:4px;top:-9px;max-width:calc(100% - 8px);padding:4px 5px;text-align:center} .approval-buttons{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:3px}.approval-buttons button{min-height:44px;min-width:0;width:100%;font-size:17px;padding:6px 0} .meal h3{font-size:14px;line-height:1.35;min-height:0}.meal .meta{font-size:11px;line-height:1.4} .cook-picker{display:grid;grid-template-columns:1fr;gap:2px;font-size:12px}.cook-picker select{width:100%;max-width:none;font-size:16px;min-height:44px;padding:6px 0 6px 3px;border-radius:6px} .meal-actions{display:grid;grid-template-columns:1fr;gap:5px;padding-top:2px}.meal-actions button{width:100%;font-size:12px;line-height:1.2;min-height:44px;padding:6px 2px;white-space:normal} .panel{padding:15px 12px;margin:12px 0}.panel h2{font-size:18px}.fields,.recipe-columns{grid-template-columns:1fr;gap:12px}.fields .full{grid-column:auto}.recipe-columns{gap:0}.recipe-list{font-size:15px;padding-left:20px;overflow-wrap:anywhere} .backup{padding:14px 12px}.backup summary{min-height:44px;align-content:center}.backup .row{display:grid;grid-template-columns:1fr}.backup select{width:100%} .shop-heading{display:block}.shop-heading h2{font-size:20px}.cart-tools{display:grid;grid-template-columns:auto minmax(0,1fr);gap:7px;margin-top:12px}.cart-tools button{font-size:13px;padding:8px 6px}.cart-note{font-size:12px;line-height:1.4;margin:8px 0 12px} .shop-group{padding:12px 0 3px}.shop-item{padding:7px 0}.shop-line{grid-template-columns:44px minmax(0,1fr) auto;gap:6px}.check-button{width:44px;height:44px;min-height:44px;border:0;background:transparent;position:relative;font-size:18px;isolation:isolate}.check-button::before{content:'';position:absolute;inset:8px;border:1.5px solid #8c9787;border-radius:50%;z-index:-1;background:white}.check-button.checked{background:transparent;color:white}.check-button.checked::before{background:#3f7152;border-color:#3f7152} .shop-name{font-size:13px;overflow-wrap:anywhere}.shop-qty{font-size:12px}.pantry-shortcut{font-size:11px;min-height:44px;max-width:79px;white-space:normal;padding:5px 6px}.shop-detail,.quick-pantry{margin-left:50px}.shop-detail summary{min-height:44px;line-height:1.4;align-content:center}.breakdown{align-items:start}.breakdown span:last-child{flex-shrink:0}.quick-pantry{padding:10px}.mini-actions button{min-height:44px} .pantry-entry,.review-item{grid-template-columns:1fr 1fr;gap:10px}.pantry-entry label:first-child,.review-item label:first-of-type{grid-column:1/-1}.pantry-entry>button,.review-item>button{grid-column:1/-1}.pantry-entry input,.review-item input{width:100%} #pantry>details>summary,#shopping>details>summary,.disclosure summary{min-height:44px;align-content:center} .table-wrap{overflow:visible}.pantry-table,.pantry-table tbody{display:block;width:100%;margin:12px 0}.pantry-table thead{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%)} .pantry-table tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin:8px 0;border:1px solid #e0e3da;border-radius:9px;padding:11px}.pantry-table td{display:block;padding:0;border:0;font-size:14px;min-width:0;overflow-wrap:anywhere}.pantry-table td:first-child{grid-column:1/-1}.pantry-table td[data-label]::before{content:attr(data-label);display:block;font-size:11px;color:#626c5d;margin:3px 0}.pantry-table td:last-child{grid-column:1/-1}.pantry-table td button{width:100%;min-height:44px;margin-top:3px}.pantry-table .unit-small{font-size:12px}.stock-status{margin:3px 0 5px} .settings-section{margin-bottom:18px;padding-bottom:18px}.settings-section h3{font-size:17px}.settings-section>.row{align-items:stretch}.settings-section>.row button{flex:1;font-size:13px}#copy-text{font-size:16px} } @media(max-width:359px){#planner{padding-inline:8px}.meal-grid{gap:4px}.meal{padding-inline:5px}.meal h3{font-size:13px}.meal-kind{font-size:9px}.tabs button{font-size:11px}.ribbon{font-size:9px}.cook-picker select{font-size:16px}.shop-line{gap:4px}} /* Navigation remains visibly interactive even when not selected. */ .tabs{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;padding:7px;border:1px solid #ccd8e4;background:#edf2f7;border-radius:12px} .tabs button{display:flex;justify-content:center;align-items:center;gap:8px;min-height:48px;background:white;border:1px solid #b8c9db;border-radius:8px;color:#315e8e;font-weight:600;box-shadow:0 1px 2px #25415d12} .tabs button:hover{background:#dde8f2;border-color:#6d8eaf}.tabs button[aria-selected=true]{background:#315e8e;border-color:#315e8e;color:white;box-shadow:0 2px 4px #25415d30}.tab-icon{width:20px;height:20px;flex-shrink:0} .cook-picker{display:flex;flex-flow:row nowrap;align-items:center;gap:3px;font-size:12px;white-space:nowrap;min-height:36px}.cook-picker span{flex-shrink:0}.cook-picker select{flex:0 1 80px;width:80px;max-width:80px;font-size:12px;min-height:36px;padding:5px 12px 5px 4px;border-radius:6px;appearance:none;background:#ffffffb8 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5'%3E%3Cpath d='m1 1 3 3 3-3' fill='none' stroke='%2353606d'/%3E%3C/svg%3E") no-repeat right 4px center} .meal .meal-title{--title-size:17;font-size:17px;line-height:22px;height:44px;min-height:44px;max-height:44px;overflow:hidden;overflow-wrap:normal;word-break:normal;flex-shrink:0}.meal-title>span{display:block;line-height:inherit} .backup h2{font-size:15px;font-weight:600}.backup h3{font-size:17px}.backup p:last-child{margin-bottom:0} @media(max-width:680px){ .tabs{gap:4px;padding:5px}.tabs button{flex-direction:column;gap:5px;min-height:70px;padding:8px 2px;font-size:11px;line-height:1.3}.tab-icon{width:20px;height:20px} .cook-picker{display:flex;flex-flow:row nowrap;gap:2px;font-size:10px;min-height:36px}.cook-picker select{flex:1;width:0;min-width:0;max-width:66px;font-size:11px;min-height:36px;padding:5px 11px 5px 2px;background-position:right 2px center} .meal .meal-title{--title-size:14;font-size:14px;line-height:18px;height:36px;min-height:36px;max-height:36px}.backup{padding:14px 12px} } @media(max-width:359px){.tabs{gap:3px;padding:4px}.tabs button{font-size:10px}.cook-picker{font-size:9px}.cook-picker select{font-size:10px;padding-left:1px;padding-right:9px;max-width:61px}} /* Quieter, denser everyday controls. */ .chef-icon{width:18px;height:18px;flex:0 0 18px;color:#52634e}.cook-picker{gap:5px}.cook-picker select{padding-left:12px;max-width:100px;width:90px} button[aria-disabled=true]{opacity:.5;cursor:help}button[aria-disabled=true]:hover{opacity:.75}.action-help h2{font-size:18px}.action-help p{margin:10px 0;font-size:14px}.action-help ol{padding-left:20px;font-size:14px;line-height:1.6} .shop-item{padding:3px 0}.shop-line{gap:7px}.shop-name{line-height:1.25}.shop-qty{line-height:1.25}.shop-group{padding:10px 0 3px}.pantry-shortcut{min-height:36px}.cart-note{margin-bottom:8px}.settings-section+.row{margin-bottom:16px} @media(max-width:680px){.cook-picker{gap:3px}.chef-icon{width:14px;height:16px;flex-basis:14px}.cook-picker select{max-width:none;font-size:11px;padding-left:9px}.shop-item{padding:1px 0}.shop-line{gap:4px}.shop-group{padding:8px 0 2px}.pantry-shortcut{min-height:44px}.shop-name{font-size:13px}.shop-qty{font-size:11px}} @media(max-width:359px){.cook-picker{gap:2px}.chef-icon{width:12px;flex-basis:12px}.cook-picker select{font-size:10px;padding-left:7.5px}} .extras-panel{margin:0 0 14px;background:#eef3f8;border:1px solid #d5e0eb}.extras-panel>label{font-weight:600}.extras-panel>p{margin:6px 0 10px}.extras-panel textarea{font-size:16px;min-height:110px;margin-bottom:10px}.extras-added{margin-top:16px;border-top:1px solid #d5e0eb;padding-top:10px}.extras-added h3{font-size:13px;margin-bottom:5px}.extras-added .row{font-size:13px;padding:3px 0}.extras-added .row span{min-width:0;overflow-wrap:anywhere;flex:1}.extras-added button{font-size:12px;min-height:40px}.add-extras-button{border-color:#315e8e;color:#315e8e;background:#f1f6fb} @media(max-width:680px){.cart-tools .add-extras-button{grid-column:1/-1}.extras-panel{padding:13px 11px}.extras-panel .row button{min-height:44px}} /* Meals open on a dedicated screen, with the schedule kept in place behind it. */ .recipe-view>.tabs,.recipe-view>header .week-bar{display:none} .recipe-view #editor{margin-top:16px;outline:none} .recipe-view .recipe-content{border:0;margin-top:20px;padding-top:0} .recipe-view .recipe-content h3{font-size:26px;line-height:1.2;margin-bottom:8px} .recipe-view .fields{border-top:1px solid #d5e0eb;padding-top:20px;margin-top:24px} .back-schedule{min-height:44px;color:#315e8e;font-weight:600} .meal{cursor:pointer}.meal select,.meal button{cursor:pointer} @media(max-width:680px){.recipe-view #editor{padding:16px 12px}.recipe-view .recipe-content h3{font-size:23px}.recipe-view .recipe-columns{grid-template-columns:1fr}.recipe-view .recipe-list{font-size:16px;line-height:1.6}.recipe-view #editor>.row{align-items:flex-start;gap:10px;flex-direction:column}} .cart-preview-row{margin:-2px 0 12px}.cart-preview-row button{font-size:12px;min-height:40px;color:#315e8e} .cart-report{padding:16px;border:1px solid #e6b7a0;border-left:4px solid #b4493d;border-radius:10px;background:#fff5ed;margin:12px 0 16px;overflow-wrap:anywhere;scroll-margin-top:16px} .cart-report:focus{outline:3px solid #86a8ce;outline-offset:3px}.cart-report h3{font-size:18px}.cart-summary{margin:6px 0 3px;font-size:14px}.cart-warning strong,.cart-reason{color:#843e25}.cart-demo-label{font-size:11px;font-weight:700;color:#315e8e;letter-spacing:.02em;margin-bottom:10px} .cart-issues{list-style:none;margin:12px 0;padding:0;border-top:1px solid #ead7c9}.cart-issues>li{padding:10px 0;border-bottom:1px solid #ead7c9}.cart-issues strong{font-size:14px;color:#252923}.cart-reason{font-size:12px}.cart-issues p{font-size:13px;margin-top:4px}.cart-issues summary,.cart-added summary{cursor:pointer;min-height:44px;align-content:center;font-size:13px;color:#315e8e}.cart-added{margin-top:8px;font-size:13px}.cart-stale{background:#fbe7d6;padding:9px;margin-top:10px;font-size:13px}.cart-complete{border-color:#9ebda1;border-left-color:#3f7152;background:#f2f7f0}.cart-count{background:#b4493d;color:white;border-radius:20px;padding:1px 6px;font-size:11px;line-height:18px}.tabs button{position:relative}#cart-notification button{width:100%;text-align:left;background:#fff3e6;color:#843e25;border-color:#e6b7a0;margin-bottom:14px;min-height:44px}.cart-report button{min-height:44px} @media(max-width:680px){.cart-report{padding:12px}.cart-report h3{font-size:17px}.cart-report .row{gap:4px}.cart-issues .row{align-items:flex-start;flex-direction:column}.cart-summary{font-size:13px}.tabs .cart-count{position:absolute;right:3px;top:3px;min-width:18px;padding:0 4px}.cart-report button[data-action=cart-copy-missing]{width:100%}} .household-settings{margin-top:20px}.household-settings h2{font-size:18px}.member-card{min-width:0;border:1px solid #d5ded7;background:#f6f8f4;border-radius:10px;padding:12px 14px;margin:18px 0}.member-card legend{font-size:15px;font-weight:600;padding:0 6px;max-width:100%;overflow-wrap:anywhere}.member-card .fields{margin:6px 0 12px;gap:10px}.member-card input,.member-card select{width:100%}.member-card details summary{cursor:pointer;min-height:44px;align-content:center;font-size:13px;color:#315e8e}.member-meals{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:13px}.member-meals label,.member-check{display:flex;align-items:center;gap:7px;min-height:44px}.member-meals input[type=checkbox],.member-check input[type=checkbox]{width:20px;height:20px;min-height:20px;flex:0 0 20px;accent-color:#315e8e}.member-remove{font-size:12px;min-height:44px;margin-top:8px;background:transparent;color:#843e25}.family-prep{margin:18px 0;padding:15px;border-radius:10px;background:#f1f6ef;border:1px solid #d5e1cf}.recipe-view .family-prep h3{font-size:17px;margin:0 0 10px}.family-person+.family-person{border-top:1px solid #dbe3d6;margin-top:12px;padding-top:12px}.family-person h4{font-size:14px;margin:0 0 5px}.family-person h4 span{font-weight:400;color:#626a61;font-size:12px}.family-person ul{margin:6px 0;padding-left:19px;font-size:13px;line-height:1.55}.family-person li{margin:4px 0}.family-person p{font-size:13px}.family-prep>.small{margin-top:12px}.family-prep details{font-size:12px;margin-top:10px}.family-prep summary{min-height:40px;align-content:center;cursor:pointer} @media(max-width:680px){.member-card{padding:10px}.member-card input,.member-card select,.member-card textarea{font-size:16px;min-height:44px}.member-card .fields{gap:10px}.member-meals{gap:4px 12px}.member-meals span{width:100%}.household-settings>.row button{flex:1}.family-prep{padding:12px}.family-person h4 span{display:block;margin-top:3px}} @media(max-width:359px){.member-card .fields{grid-template-columns:1fr}} /* Stable mobile controls and an honest in-progress swap state. */ .meal-actions{margin-top:0}.meal.is-swapping{background:#ececea;border-color:#aaa;color:#777}.meal.is-swapping .meal-title,.meal.is-swapping .meal-kind{color:#777}.meal.is-swapping .ribbon{background:#777}.meal.is-swapping .approval-buttons button{background:#eee}.meal.is-swapping .repeat{background:#eee;border-color:#bbb;color:#777}.meal.is-swapping .meta{opacity:.7} .meal .meta{display:flex;flex-wrap:wrap;column-gap:3px;row-gap:2px;align-items:center}.meal-time,.meal-servings{white-space:nowrap} .cook-picker{position:relative}.cook-select{display:flex;align-items:center;justify-content:space-between;gap:3px;min-width:0;flex:1;font-size:12px;min-height:40px;padding:5px 7px 5px 12px;text-align:left;border-radius:6px;background:#ffffffb8}.cook-select>span:first-child{min-width:0;overflow:hidden;text-overflow:ellipsis}.cook-chevron{font-size:12px;color:#53606d}.cook-options{position:absolute;top:calc(100% + 4px);left:0;right:0;min-width:110px;background:#fff;border:1px solid #a7bacb;border-radius:8px;box-shadow:0 5px 15px #20334233;padding:4px;z-index:20}.cook-options button{display:block;width:100%;min-height:44px;padding:7px 12px;text-align:left;font-size:13px;border:0;background:white}.cook-options button[aria-selected=true]{background:#e3edf6}.cook-options button:hover,.cook-options button:focus-visible{background:#dce9f4}.meal:has(.cook-options){z-index:21} .recipe-hero{margin:14px 0 12px}.recipe-hero img{display:block;width:100%;height:auto;max-height:320px;object-fit:cover;border-radius:10px}.recipe-hero figcaption{font-size:11px;color:#687064;margin:5px 0 0} @media(max-width:680px){.cook-select{font-size:11px;min-height:44px;padding-left:9px;padding-right:4px}.meal .meta{min-height:33px;align-content:center}.meta-divider{display:none}.meal-time,.meal-servings{display:block;width:100%}.recipe-hero img{max-height:none;aspect-ratio:3/2}.meal-actions button{min-height:44px}} @media(prefers-reduced-motion:reduce){.meal{transition:none}} @font-face{font-family:'Meal Emoji';src:url('data:font/woff2;base64,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')} .approval-buttons button{font-family:'Meal Emoji',system-ui,sans-serif} /* Persistent action labels: state changes never move or rename the controls. */ .meal-grid{grid-template-columns:repeat(var(--meal-columns,3),minmax(0,1fr))} .meal-actions button[aria-disabled=true]{opacity:1;background:#e8e9e5;color:#92958e;border-color:#d5d7d0;cursor:help} .meal-actions .mark-cooked.done{background:#3f7152;color:white;border-color:#3f7152;opacity:1} .meal-actions .repeat.active{color:#3f7152} .swap-help{font-size:11px;line-height:1.35;color:#646761;min-width:0} .refresh-swap{font-size:11px!important;line-height:1.35!important;padding:3px!important;min-height:44px;background:transparent!important;border:0;text-decoration:underline;text-underline-offset:2px;white-space:normal} .ribbon{white-space:nowrap}.meal.is-swapping .ribbon{background:#737670} .meal.is-swapping .approval-buttons button{color:#8a8d86;border-color:#d5d7d0;opacity:.7} .schedule-section .fields{align-items:start}.schedule-section .status{font-size:13px;margin:10px 0;color:#626a61} @media(max-width:680px){#planner{padding-top:16px}h1{margin-bottom:12px}.tabs{margin-top:14px;margin-bottom:12px}.day-title{margin-bottom:14px}} .swap-icon{display:inline-block}.swap-preparing .swap-icon{animation:swap-turn .85s linear infinite}@keyframes swap-turn{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.swap-preparing .swap-icon{animation:none}} </style></head> <body><main id="planner"> <header><h1>Weekly Meal Plan</h1><div class="week-bar"><h2 id="week-label"></h2><div class="week-picker"><button data-action="prev" aria-label="Previous week">←</button><input type="date" id="week-date" aria-label="Choose a week"><button data-action="next" aria-label="Next week">→</button></div></div></header> <nav class="tabs" role="tablist" aria-label="Meal planner"><button id="tab-meals" role="tab" aria-controls="meals" data-tab="meals" aria-selected="true"><svg class="tab-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true"><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M7 3v4m10-4v4M3 11h18m-13 4h2m4 0h2"/></svg><span>Meal Schedule</span></button><button id="tab-shopping" role="tab" aria-controls="shopping" data-tab="shopping" aria-selected="false"><svg class="tab-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true"><path d="m3 4 2 1 3 12h11l2-9H6M9 21h.01M18 21h.01"/></svg><span>Shopping List</span></button><button id="tab-pantry" role="tab" aria-controls="pantry" data-tab="pantry" aria-selected="false"><svg class="tab-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true"><rect x="5" y="6" width="14" height="15" rx="2"/><path d="M8 3h8M5 12h14m-9 4h4"/></svg><span>My Pantry</span></button><button id="tab-settings" role="tab" aria-controls="settings" data-tab="settings" aria-selected="false"><svg class="tab-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/><circle cx="8" cy="6" r="2" fill="currentColor"/><circle cx="16" cy="12" r="2" fill="currentColor"/><circle cx="10" cy="18" r="2" fill="currentColor"/></svg><span>Settings</span></button></nav> <div id="message" role="status" aria-live="polite"></div><div id="conflict" class="notice" hidden>A newer version was saved in another tab. <button data-action="reload">Load latest</button></div> <div id="cart-notification" role="status" aria-live="polite" aria-atomic="true" hidden></div> <section id="action-help" class="panel action-help" hidden tabindex="-1" aria-live="polite" aria-label="Action help"></section> <section id="review" class="panel" hidden aria-label="Review change"></section> <section id="editor" class="panel" hidden aria-label="Meal and recipe editor"></section> <section id="meals" role="tabpanel" aria-labelledby="tab-meals"></section> <section id="shopping" role="tabpanel" aria-labelledby="tab-shopping" hidden></section> <section id="pantry" role="tabpanel" aria-labelledby="tab-pantry" hidden></section> <section id="settings" role="tabpanel" aria-labelledby="tab-settings" hidden></section> <section id="copy-box" class="panel" hidden><div class="row between"><h3 id="copy-title">Copy to this chat</h3><button data-action="close-copy">Close</button></div><textarea id="copy-text" readonly aria-label="Text to copy"></textarea></section> </main><script id="meal-config"> // Fill from the user’s confirmed inputs. No demo household data. window.MEAL_CONFIG={ "ready": false, "demo": false, "storageKey": "weekly-meal-plan-v3", "firstWeek": null, "profile": { "names": [], "members": [], "allergies": "", "restrictions": "", "preferences": "", "breakfastLimit": 20, "lunchLimit": 20, "dinnerLimit": 30, "servings": { "breakfast": 1, "lunch": 1, "dinner": 1 }, "mealKinds": [ "breakfast", "lunch", "dinner" ], "budget": "", "planner": "", "shopper": "", "timezone": "", "otherStores": "" }, "store": { "name": "", "branch": "", "zip": "", "method": "Pickup", "confirmed": false, "url": null }, "choices": { "breakfast": [], "lunch": [], "dinner": [] }, "pantry": {} }; </script><script id="meal-recipes"> // Populate with reviewed recipes for this household before ready=true. window.MEAL_DATA={ITEMS:{},R:{},CARD_NAMES:{}}; </script><script id="meal-cart-results"> /* Cart receipts only. Never shops, purchases, or changes pantry stock. */ (() => { 'use strict'; const reasons = {out_of_stock:'Out of stock',not_found:'No matching product found',error:'Store connection failed',not_attempted:'Not attempted',unknown:'Could not confirm this item',partial:'Only part of the quantity was added'}; const safe = text => String(text ?? '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); const number = value => Number.isFinite(value) && value >= 0; const round = value => Math.round(value * 1000000) / 1000000; function normalize(input) { if (!input || typeof input.runId !== 'string' || !input.runId || !/^\d{4}-\d{2}-\d{2}$/.test(input.week) || typeof input.listKey !== 'string' || !input.listKey || typeof input.store !== 'string' || !input.store || !Number.isFinite(Date.parse(input.finishedAt)) || !Array.isArray(input.requested) || !input.requested.length || input.requested.length > 500 || !Array.isArray(input.results)) throw new Error('Incomplete cart receipt.'); const ids = new Set(), outcomes = new Map(); for (const result of input.results) { if (!result || typeof result.id !== 'string' || outcomes.has(result.id)) throw new Error('Duplicate or invalid cart result.'); outcomes.set(result.id, result); } const lines = input.requested.map(item => { if (!item || typeof item.id !== 'string' || !item.id || ids.has(item.id) || typeof item.name !== 'string' || !item.name || typeof item.unit !== 'string' || !number(item.qty) || item.qty <= 0) throw new Error('Invalid requested item.'); ids.add(item.id); const result = outcomes.get(item.id); const verified = result?.confirmed === true && number(result.addedQty) && result.addedQty <= item.qty; const added = verified ? result.addedQty : 0; const remaining = round(item.qty - added); let reason = remaining ? (reasons[result?.status] ? result.status : added > 0 ? 'partial' : 'unknown') : 'added'; if (result && !verified && result.addedQty > 0) reason = 'unknown'; return {id:item.id,name:item.name,unit:item.unit,requested:item.qty,added,remaining,reason,substitute:typeof result?.substitute==='string'?result.substitute.slice(0,300):''}; }); if ([...outcomes.keys()].some(id => !ids.has(id))) throw new Error('Unexpected item in cart receipt.'); return {runId:input.runId,week:input.week,listKey:input.listKey,store:input.store,finishedAt:input.finishedAt,lines}; } function summary(report) { const missing = report.lines.filter(line => line.remaining > 0); return {total:report.lines.length,complete:report.lines.length-missing.length,partial:missing.filter(line=>line.added>0).length,missing}; } function missingText(report) { return `${report.store} — items still needing attention\n` + summary(report).missing.map(line => `${line.name}: ${line.remaining} ${line.unit} still unconfirmed (${line.added} of ${line.requested} confirmed added). ${reasons[line.reason] || reasons.unknown}.` ).join('\n') + '\nCheck the existing cart before retrying an unconfirmed item. Do not duplicate confirmed quantities.'; } function html(report,{demo=false,stale=false}={}) { const s=summary(report),warning=s.missing.length>0||stale; const title=stale?'Cart result needs a fresh check':s.missing.length?'Cart needs attention':'Requested items added'; return `<section id="cart-report" class="cart-report ${warning?'cart-warning':'cart-complete'}" aria-labelledby="cart-report-title" tabindex="-1"> ${demo?'<div class="cart-demo-label">DEMO ONLY · No items were added to a real cart</div>':''} <div class="row between"><h3 id="cart-report-title">${warning?'⚠':'✓'} ${title}</h3>${demo?'<button data-action="cart-demo-close" aria-label="Close demo notification">Close demo</button>':''}</div> <p class="cart-summary">${s.complete} of ${s.total} items fully added${s.missing.length?` · <strong>${s.missing.length} need attention</strong>`:''}${s.partial?` (${s.partial} partly added)`:''}</p> <p class="small muted">${safe(report.store)} · ${safe(new Date(report.finishedAt).toLocaleString('en-US'))}</p> ${stale?'<p class="cart-stale">Your shopping list or store has changed since this result. Check the current cart before retrying.</p>':''} ${s.missing.length?`<ul class="cart-issues">${s.missing.map(line=>`<li><div class="row between"><strong>${safe(line.name)}</strong><span class="cart-reason">${safe(reasons[line.reason]||reasons.unknown)}</span></div><p>${line.added} of ${line.requested} ${safe(line.unit)} confirmed added · ${line.remaining} still needed</p>${line.substitute?`<details><summary>Review suggested substitute</summary><p>${safe(line.substitute)}</p><p class="small muted">Have your AI check the product, price and dietary requirements before you approve it. Reviewing this option does not add it.</p></details>`:''}</li>`).join('')}</ul><p class="small">These items stay on your shopping list. Nothing will be substituted automatically.</p><button class="space-top" data-action="cart-copy-missing">Copy items needing attention</button>`:'<p class="small space-top">Review your store cart before checking out. Added to cart does not mean purchased.</p>'} ${s.complete?`<details class="cart-added"><summary>View confirmed additions (${s.complete})</summary><ul>${report.lines.filter(line=>!line.remaining).map(line=>`<li>${safe(line.name)} · ${line.added} ${safe(line.unit)}</li>`).join('')}</ul></details>`:''} </section>`; } function demo(week,listKey) { return normalize({runId:'demo',week,listKey,store:'Example grocery store',finishedAt:new Date().toISOString(), requested:[{id:'sample-eggs',name:'Eggs',qty:1,unit:'carton'},{id:'sample-avocado',name:'Avocados',qty:3,unit:'avocados'},{id:'sample-tortilla',name:'Tortillas',qty:2,unit:'packs'}], results:[{id:'sample-eggs',confirmed:true,addedQty:1},{id:'sample-avocado',confirmed:true,addedQty:0,status:'out_of_stock',substitute:'Example option: a different avocado variety. Availability and price have not been checked.'},{id:'sample-tortilla',confirmed:true,addedQty:1,status:'out_of_stock'}]}); } const api={normalize,summary,missingText,html,demo}; if(typeof module!=='undefined'&&module.exports)module.exports=api; if(typeof window!=='undefined')window.CartResults=api; })(); </script><script id="meal-household"> /* Household planning inputs. Ages never determine nutrition targets or readiness. */ (() => { 'use strict'; const groups={adult:'Adult',teen:'Teen (13–17)',child:'Child (4–12)',toddler:'Toddler (1–3)',baby:'Baby (under 1)'}; const kinds=['breakfast','lunch','dinner']; const ranges={baby:[0,11],toddler:[12,47],child:[4,12],teen:[13,17],adult:[18,120]}; const esc=x=>String(x??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); const copy=x=>JSON.parse(JSON.stringify(x)); const months=m=>m.age===null?null:m.age*(m.group==='baby'||m.group==='toddler'?1:12); const familyEater=m=>m.group!=='baby'&&m.feeding==='family'; const ageText=m=>m.age===null?groups[m.group]:`${m.age} ${m.group==='baby'||m.group==='toddler'?'months':'years'}`; const blank=(id,name='',group='child')=>({id,name,group,age:null,feeding:group==='baby'?'unknown':'family',portion:group==='adult'?1:null,meals:[...kinds],heat:'usual',separate:false,needs:''}); function initial(profile){return copy(profile.members||profile.names.map((name,i)=>blank('member-'+i,name,'adult')))} function validate(members){ if(!Array.isArray(members)||!members.length||members.length>20)throw new Error('Add between 1 and 20 household members.'); const ids=new Set(); return members.map(member=>{ const m=copy(member);m.name=String(m.name||'').trim(); if(!m.id||ids.has(m.id)||!m.name||!groups[m.group])throw new Error('Give each person a name and age group.');ids.add(m.id); const [min,max]=ranges[m.group]; if(m.age===null&&m.group!=='adult'||m.age!==null&&(!Number.isInteger(m.age)||m.age<min||m.age>max))throw new Error(`Check ${m.name}’s age. Use ${min}–${max} ${m.group==='baby'||m.group==='toddler'?'months':'years'}, or choose another age group.`); if(!['family','milk','separate'].includes(m.feeding))throw new Error(`Choose ${m.name}’s current feeding stage.`); if(m.group==='baby'&&m.feeding==='family')throw new Error('Keep baby feeding separate from adult recipe portions.'); if(m.group==='baby'&&m.feeding==='separate'&&m.age<4)throw new Error('For a baby under 4 months, keep an individual clinician-directed plan outside these family recipes.'); if(m.feeding==='separate'&&!String(m.needs||'').trim())throw new Error(`Add the established foods, textures or caregiver instructions for ${m.name}’s separate plan.`); if(!Array.isArray(m.meals)||m.meals.some(k=>!kinds.includes(k)))throw new Error('Choose valid meal types.');m.meals=[...new Set(m.meals)]; if(familyEater(m)&&(!Number.isFinite(m.portion)||m.portion<0.25||m.portion>5||m.portion*4%1!==0))throw new Error(`Choose ${m.name}’s recipe portion in 0.25 steps, between 0.25 and 5. This is a cooking estimate, not a nutrition target.`); if(!familyEater(m))m.portion=0; if(!['usual','mild','side'].includes(m.heat))m.heat='usual';m.needs=String(m.needs||'').slice(0,2000);m.separate=!!m.separate; return m; }); } function participants(members,kind){return members.filter(m=>m.meals.includes(kind))} function portions(members,kind){return Math.round(participants(members,kind).filter(familyEater).reduce((n,m)=>n+m.portion,0)*100)/100} function prepareWeek(week,members,{scale=false}={}){ const result=copy(week); for(const m of result.meals){ if(m.status==='cooked'||!['planned','empty'].includes(m.status)&&!m.householdExcluded)continue; m.familyMembers=copy(participants(members,m.kind)); if(scale){const total=portions(members,m.kind);if(total>12)throw new Error('This planner supports up to 12 recipe servings per meal. Adjust the household portions.'); if(total===0){m.householdPreviousStatus=m.householdPreviousStatus||m.status;m.householdExcluded=true;m.status='away';m.servings=0} else{if(m.householdExcluded){m.status=m.householdPreviousStatus||'planned';delete m.householdExcluded;delete m.householdPreviousStatus}m.servings=total} } if(m.status!=='cooked')m.approval='pending'; } return result; } function notes(m,recipe={ing:{}}){ const ing=recipe.ing||{},result=[]; if(m.feeding==='milk')return ['Milk feeding only: not included in recipe portions. Follow the baby’s existing feeding plan.']; if(m.feeding==='separate'){ result.push('Use the established separate food plan below. This family recipe is not automatically a baby-food recipe.'); if(months(m)!==null&&months(m)<12&&ing.honey)result.push('This recipe contains honey. Do not include honey in food for a baby under 12 months.'); result.push('Include any separately planned foods in the shopping list; these notes alone do not add groceries.'); if(m.needs)result.push(m.needs);return result; } const young=m.group==='toddler'||m.group==='child'&&m.age!==null&&m.age<5; if(m.heat==='mild'||m.heat==='side'||young)result.push('Set aside this portion before adding hot sauce, chili or salty sauces. Season other portions separately.'); if(m.separate||young)result.push('Serve the components separately if preferred. Adapt size and texture to this child’s eating skills.'); if(young){ if(ing.beans||ing.chickpeas)result.push('Cook beans or chickpeas until soft and mash them; do not serve whole beans.'); if(ing.carrot||ing.cabbage||ing.cucumber||ing.pepper||ing.broccoli||ing.potato)result.push('Soften hard vegetables and adapt the texture. Do not serve hard raw chunks.'); if(ing.chicken||ing.beef||ing.salmon)result.push('Remove bones; make cooked meat or fish soft and easy to eat rather than tough or large pieces.'); if(ing.chorizo)result.push('Choose a different protein for the child’s portion; this recipe uses salty sausage. Ask your AI to revise ingredients and the shopping list.'); if(ing.cornTortilla)result.push('Skip crisp tostada shells for this child; use suitably soft components.'); result.push('Have the child sit upright and supervise throughout the meal. These checks are not a complete safety assessment.'); } if(m.needs)result.push(m.needs); return result; } function recipeHTML(meal,profile,recipe){ const members=meal.familyMembers||(meal.status==='cooked'?[]:participants(profile.members||[],meal.kind)); if(!members.length)return ''; return `<section class="family-prep"><h3>For your household</h3>${members.map(m=>`<div class="family-person"><h4>${esc(m.name)} <span>${esc(ageText(m))}${familyEater(m)?` · usual portion ${m.portion}`:''}</span></h4>${notes(m,recipe).length?`<ul>${notes(m,recipe).map(t=>`<li>${esc(t)}</li>`).join('')}</ul>`:'<p>Use the recipe with this person’s preferences.</p>'}</div>`).join('')}<p class="small muted">Recipe quantities above are the total to cook. Individual portions are editable planning estimates.</p>${members.some(m=>m.group==='baby'||m.group==='toddler')?'<details><summary>Baby and toddler feeding guidance</summary><a href="https://www.cdc.gov/infant-toddler-nutrition/foods-and-drinks/when-what-and-how-to-introduce-solid-foods.html" target="_blank" rel="noopener noreferrer">CDC: readiness and food preparation</a><br><a href="https://www.cdc.gov/infant-toddler-nutrition/foods-and-drinks/choking-hazards.html" target="_blank" rel="noopener noreferrer">CDC: choking hazards</a></details>':''}</section>`; } function recipeOptions(recipes,members,current){ const family=members.some(m=>familyEater(m)&&(m.group==='toddler'||m.group==='child')); const flexible=['potato','burrito','chickenBowl','lunchBowl','tacoBowl','salmon']; const entries=Object.entries(recipes),option=([id,r])=>`<option value="${esc(id)}" ${id===current?'selected':''}>${esc(r.name)}</option>`; return family?'<optgroup label="Easy to serve as separate components">'+entries.filter(([id])=>flexible.includes(id)).map(option).join('')+'</optgroup><optgroup label="Other recipes — review preparation">'+entries.filter(([id])=>!flexible.includes(id)).map(option).join('')+'</optgroup>':entries.map(option).join(''); } const select=(field,values,current)=>`<select data-person-field="${field}">${Object.entries(values).map(([value,label])=>`<option value="${value}" ${value===current?'selected':''}>${label}</option>`).join('')}</select>`; function settingsHTML(members){ return `<section class="settings-section household-settings"><div class="row between"><h2>People &amp; ages</h2><button data-action="member-add">Add person</button></div><p class="small muted space-top">Plan for each person’s age, appetite and usual meals. Use months for babies and toddlers. Adult ages are optional.</p><div id="household-members">${members.map(m=>`<fieldset class="member-card" data-member="${esc(m.id)}"><legend>${esc(m.name||'New person')}</legend><div class="fields"><label>Name<input data-person-field="name" maxlength="80" value="${esc(m.name)}"></label><label>Age group${select('group',groups,m.group)}</label><label>Age in ${m.group==='baby'||m.group==='toddler'?'months':'years'}<input data-person-field="age" type="number" min="${ranges[m.group][0]}" max="${ranges[m.group][1]}" step="1" value="${m.age===null?'':m.age}" placeholder="${m.group==='adult'?'Optional':'Required'}"></label><label>Current feeding${select('feeding',m.group==='baby'?{unknown:'Choose feeding stage',milk:'Breast milk / formula only',separate:'Established baby food plan'}:{family:'Family meals',separate:'Separate food plan',milk:'Milk feeding only'},m.feeding)}</label>${familyEater(m)?`<label>Recipe portion<input data-person-field="portion" type="number" min="0.25" max="5" step="0.25" value="${m.portion??''}" placeholder="Choose amount"></label><label>Spice${select('heat',{usual:'Usual family preference',mild:'Mild',side:'Hot sauce on the side'},m.heat)}</label>`:'<p class="small full">Not counted in family-recipe servings. No milk amounts or new feeding stages are generated.</p>'}</div><div class="member-meals"><span>Include in</span>${kinds.map(k=>`<label><input type="checkbox" data-person-meal="${k}" ${m.meals.includes(k)?'checked':''}>${k[0].toUpperCase()+k.slice(1)}</label>`).join('')}</div><details ${m.feeding==='separate'?'open':''}><summary>Preferences &amp; preparation</summary><label class="member-check"><input type="checkbox" data-person-field="separate" ${m.separate?'checked':''}>Serve components separately</label><label>${m.feeding==='separate'?'Established foods, textures and caregiver / clinician instructions':'Individual allergies, dislikes, texture needs or prep notes'}<textarea data-person-field="needs" maxlength="2000" placeholder="${m.feeding==='separate'?'Use the feeding plan you have already established.':'For example: no nuts; mild food; school provides lunch.'}">${esc(m.needs)}</textarea></label></details><button class="member-remove" data-action="member-remove" data-id="${esc(m.id)}" aria-label="Remove ${esc(m.name||'person')} from household">Remove person</button></fieldset>`).join('')}</div><p class="small muted">Recipe portions are cooking estimates, not nutrition targets. Choose amounts from appetite and leftovers; 0.5 means half a recipe serving. Ages do not set portions automatically.</p><label class="member-check"><input id="household-scale" type="checkbox">Update this week’s recipe quantities from these portions</label><p class="small muted">Otherwise, keep the quantities already planned. Cooked meals and pantry stock stay unchanged.</p><div class="row space-top"><button class="primary" data-action="household-save">Review household changes</button><button data-action="household-copy">Copy household for AI</button></div></section>`; } function brief(members){return 'Use these household members when selecting meals. Prefer one adaptable family meal where practical; use age, confirmed feeding stage, individual needs and meal attendance. Portions are user estimates, not nutrition requirements. Do not infer solids readiness, new allergens, milk amounts or a medical diet. Keep milk-only babies out of family recipe quantities. For babies eating solids, use established caregiver instructions and a separate appropriate preparation plan; include its actual ingredients without double-counting. Preserve existing allergies and check any uncertain food preparation before approval.\n\n'+members.map(m=>`${m.name}: ${ageText(m)}; feeding: ${m.feeding}; meals: ${m.meals.join(', ')||'none'}; ${familyEater(m)?'recipe portion: '+m.portion:'not counted in family recipe portions'}; spice: ${m.heat}; separate components: ${m.separate?'yes':'no'}; individual notes: ${m.needs||'not provided; confirm any essential restrictions'}`).join('\n')} const api={groups,blank,initial,validate,months,familyEater,participants,portions,prepareWeek,notes,recipeHTML,recipeOptions,settingsHTML,brief}; if(typeof module!=='undefined'&&module.exports)module.exports=api; if(typeof window!=='undefined')window.Household=api; })(); </script><script id="meal-meal-replacements"> (function(root){ 'use strict'; const none=value=>!value||/^(none|no|n\/a)$/i.test(String(value).trim()); function replace(meal,catalog,profile={},week=[],requiresReview=false){ if(meal.status!=='planned')return {meal,changed:false}; const next=JSON.parse(JSON.stringify(meal)); next.rejectedRecipes=[...new Set([...(next.rejectedRecipes||[]),next.recipe])]; next.approval='rejected';next.repeat=false; const members=next.familyMembers||profile.members||[]; const needsReview=requiresReview||!none(profile.allergies)||!none(profile.restrictions)||members.some(m=>!none(m.needs)); const limit=Number(profile[meal.kind+'Limit'])||Infinity; const used=new Set(week.filter(m=>m.id!==meal.id).map(m=>m.recipe)); const candidates=Object.keys(catalog).filter(id=>!next.rejectedRecipes.includes(id)&&(catalog[id].kind===meal.kind||catalog[id].kind==='either')&&catalog[id].total<=limit); candidates.sort((a,b)=>Number(used.has(a))-Number(used.has(b))); if(needsReview||!candidates.length){ next.replacementMessage=needsReview?'Your dietary or preparation requirements need a recipe review. Ask your AI to review them and supply a matching replacement.':'No more matching alternatives are saved. Ask your AI for a new recipe, or use Undo to restore the previous meal.'; return {meal:next,changed:false}; } next.recipe=candidates[0];next.approval='pending';delete next.replacementMessage; return {meal:next,changed:true}; } const api={replace};if(typeof module==='object'&&module.exports)module.exports=api;else root.MealReplacements=api; })(typeof window!=='undefined'?window:globalThis); </script><script id="meal-cart-handoff"> (function(root){ 'use strict'; function prepare(state,week,{ledger,meta,buyQty}){ if(!state.store.name?.trim()||!state.store.branch?.trim()||!state.store.method?.trim())throw Error('Choose your store, branch and pickup or delivery option in Settings.'); if(state.requiresReview)throw Error('Review the changed dietary requirements before preparing your cart.'); const selected=state.weeks[week]; if(!selected)throw Error('Choose a meal-plan week first.'); const accepted=selected.meals.filter(m=>m.status==='planned'&&m.approval==='accepted'&&m.recipe); if(!accepted.length&&!selected.manual?.length)throw Error('Accept the meals you want in Meal Schedule, or use Add extra items, then prepare your cart request.'); const copy=JSON.parse(JSON.stringify(state)); for(const w of Object.values(copy.weeks))w.meals=w.meals.filter(m=>m.status==='planned'&&m.approval==='accepted'); for(const stock of Object.values(copy.stock))if(stock.demo)stock.qty=0; const demand=ledger(copy).weeks[week]||{}; const pantryChecks=Object.keys(demand).filter(id=>state.stock[id]?.demo||state.stock[id]?.qty===null||state.stock[id]?.lowerBound).map(id=>({name:meta(id).name,reason:state.stock[id].demo?'demo amount — not counted':state.stock[id].qty===null?'amount unknown':'minimum amount only'})); const requested=Object.entries(demand).filter(([,row])=>row.need>0).map(([id,row])=>({id,name:meta(id).name,qty:buyQty(id,row),unit:meta(id).unit})); return {week,store:{name:state.store.name,branch:state.store.branch,zip:state.store.zip,method:state.store.method},acceptedMeals:accepted.length,excludedMeals:selected.meals.filter(m=>m.status==='planned'&&m.approval!=='accepted').length,requested,pantryChecks,allergies:state.profile.allergies,restrictions:state.profile.restrictions,budget:state.profile.budget||'Not specified',revision:state.rev}; } function text(p){ const direct=p.store.name.trim().toLowerCase()==='safeway'; return [ 'Prepare my '+p.store.name+' cart — review products before adding.', 'Week: '+p.week+' | Planner revision: '+p.revision, 'Destination: '+[p.store.name,p.store.branch,p.store.zip,p.store.method].filter(Boolean).join(' · '), direct?'Use Safeway.com directly. Use my signed-in Safeway browser session if it is still valid; otherwise request secure sign-in. Do not switch to Instacart unless I ask.':'Use the selected retailer’s supported direct shopping route when available.', 'Included: '+p.acceptedMeals+' accepted meals plus extra items. '+p.excludedMeals+' unapproved/rejected meals excluded. Confirm this is the week I intend to shop for.', 'Allergies: '+p.allergies+' | Dietary requirements: '+p.restrictions+' | Weekly grocery budget: '+p.budget, p.pantryChecks.length?'Confirm pantry amounts before shopping: '+p.pantryChecks.map(x=>x.name+' ('+x.reason+')').join('; ')+'. Example pantry amounts have not reduced this request.':'Recorded pantry quantities have reduced these needs; confirm they are current.', 'NEEDS AFTER RECORDED PANTRY:', ...(p.requested.length?p.requested.map(x=>'□ '+x.name+' — '+x.qty+' '+x.unit):['No additional items needed from the recorded quantities.']), 'Capability check: Use supported browser actions and a secure signed-in retailer session. If those are unavailable, say so and provide a usable shopping list; do not claim any cart additions.', 'Workflow: Verify the exact branch and fulfillment option. Read my existing cart first; preserve its contents and ask whether overlapping quantities count toward this week. Match these ingredient quantities to actual products, package sizes and package counts. Show prices and any uncertain conversions, substitutions or out-of-stock items for my approval before adding.', 'After approval, add only the agreed outstanding quantities. Verify item-by-item in the actual cart. Report added, partial, unavailable, not attempted and unconfirmed items with remaining quantities. Reconcile uncertain additions before retrying to prevent duplicates.', 'Do not place the order, reserve a paid slot, enroll in Schedule & Save, or mark groceries purchased or added to my pantry. This request is preparation, not a cart receipt. The planner button cannot launch a browser agent by itself.' ].join('\n\n'); } const api={prepare,text}; if(typeof module==='object'&&module.exports)module.exports=api; else root.CartHandoff=api; })(typeof window==='object'?window:this); </script><script id="meal-meal-swaps"> (function(root){ 'use strict'; const clone=value=>JSON.parse(JSON.stringify(value)); function begin(meal){ if(meal.status!=='planned'||meal.swap)return clone(meal); return {...clone(meal),approval:'rejected',repeat:false,swap:{status:'preparing',originalRecipe:meal.recipe}}; } function prepare(meal,catalog,profile,week,requiresReview,replace){ if(!meal.swap||meal.recipe!==meal.swap.originalRecipe)return clone(meal); const result=replace(meal,catalog,profile,week,requiresReview),next=clone(meal); next.rejectedRecipes=result.meal.rejectedRecipes; next.swap=result.changed?{...next.swap,status:'ready',recipe:result.meal.recipe}:{...next.swap,status:'blocked',reason:result.meal.replacementMessage}; return next; } function refresh(meal,catalog,profile,week,requiresReview,replace){ if(!meal.swap||meal.swap.status==='blocked')return clone(meal); // Recheck current requirements and preserve the current cook, date and portions. const result=replace(meal,catalog,profile,week,requiresReview); if(!result.changed)return {...clone(meal),swap:{...meal.swap,status:'blocked',reason:result.meal.replacementMessage}}; const next=result.meal;delete next.swap;delete next.replacementMessage;return next; } const api={begin,prepare,refresh};if(typeof module==='object'&&module.exports)module.exports=api;else root.MealSwaps=api; })(typeof window!=='undefined'?window:globalThis); </script><script id="meal-schedule"> (function(root){ 'use strict'; const days=['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']; function validate(value){ const s={...value};for(const k of ['draftDay','reviewDay'])if(!days.includes(s[k]))throw Error('Choose a day for both the weekly draft and approval reminder.'); for(const k of ['draftTime','reviewTime'])if(!/^([01]\d|2[0-3]):[0-5]\d$/.test(s[k]))throw Error('Choose a time for both recurring tasks.'); try{new Intl.DateTimeFormat('en-US',{timeZone:s.timezone}).format()}catch{throw Error('Enter a valid timezone, such as America/Los_Angeles.');} if(!s.owner?.trim())throw Error('Choose who should receive the approval reminder.'); return {...s,status:'not-scheduled'}; } function request(s){return `Help me set up recurring meal planning in this AI environment. These are preferences, not already-created tasks. Timezone: ${s.timezone} Weekly draft: ${s.draftDay} at ${s.draftTime} Approval reminder: ${s.reviewDay} at ${s.reviewTime} Planning owner: ${s.owner} Shopping or pickup day: ${s.shoppingDay||'Ask me'} After-pickup pantry check: ${s.pantryCheck?'Offer to schedule it around my actual pickup time':'Not requested'} First check this environment's actual scheduled-task capability and inspect existing tasks to avoid duplicates. Ask for my preferred supported notification channel and any missing schedule details. Use the household, pantry, accepted meals and shopping state from my current planner. A local browser snapshot is not an unattended data source: establish a supported private durable data path before promising autonomous updates or approval-aware reminders. If that is unavailable, explain it and offer an ordinary reminder or on-demand update, accurately labeled. Create a new draft for the next upcoming Monday; preserve manual changes, accepted/cooked meals, repeats, skipped weeks, restrictions and family preparation requirements. Never replace an approved week silently. Recheck approval before sending the reminder; suppress it when everything is already approved or the week is paused. The reminder says to review meals and pantry, then approve the shopping list before items can be added to the selected store cart. Approval, cart additions and checkout are separate actions. Do not add products or check out from a recurring task without separate, applicable authorization. If the draft failed, report that failure instead of saying it is ready. Use unique week/run IDs and account for daylight saving. Report task IDs, next runs and timezone only after the scheduler confirms creation. Let me pause or change each task. After pickup, reconcile only the confirmed received groceries; a cart receipt is not pantry stock.`;} function html(state,esc,options){ const s=state.schedulePreferences||{},day=(id,value)=>`<select id="${id}"><option value="">Choose a day</option>${options(days,value)}</select>`; return `<section class="settings-section schedule-section"><h3>Weekly planning & reminders</h3><p class="status">${s.status==='not-scheduled'?'Preferences saved · tasks not scheduled yet':'Choose when your AI should prepare meals and remind you to approve.'}</p><div class="fields"><label>Prepare next week’s plan${day('schedule-draft-day',s.draftDay)}</label><label>At<input id="schedule-draft-time" type="time" value="${esc(s.draftTime||'')}"></label><label>Remind me to approve${day('schedule-review-day',s.reviewDay)}</label><label>At<input id="schedule-review-time" type="time" value="${esc(s.reviewTime||'')}"></label><label>Timezone<input id="schedule-timezone" value="${esc(s.timezone||state.profile.timezone)}"></label><label>Planning owner<select id="schedule-owner">${options(state.profile.names,s.owner||state.profile.planner)}</select></label><label>Shopping or pickup day${day('schedule-shopping-day',s.shoppingDay)}</label><label class="member-check"><input type="checkbox" id="schedule-pantry-check" ${s.pantryCheck?'checked':''}>Offer a pantry check after pickup</label></div><div class="row"><button data-action="schedule-save">Save preferences</button><button data-action="schedule-copy">Set up with my AI</button></div><p class="small muted space-top">This prepares a setup request for your AI. Tasks are active only after its scheduler confirms them.</p></section>`; } const api={validate,request,html};if(typeof module==='object'&&module.exports)module.exports=api;else root.MealSchedule=api; })(typeof window!=='undefined'?window:globalThis); </script><script id="meal-app"> (()=>{ 'use strict'; const CONFIG=window.MEAL_CONFIG||{}; if(CONFIG.ready===false){document.getElementById('planner').innerHTML='<h1>Weekly Meal Plan</h1><p>Ask your AI to personalize this template with your household inputs, then open your completed planner.</p>';return} const {ITEMS,R}=window.MEAL_DATA; const cardNames=window.MEAL_DATA.CARD_NAMES||{}; const $=id=>document.getElementById(id),root=$('planner'),clone=x=>JSON.parse(JSON.stringify(x)); const esc=x=>String(x??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); const num=x=>Math.round(Number(x)*100)/100,fmt=x=>String(num(x)); const iso=d=>d.toISOString().slice(0,10),day=d=>new Date(d+'T12:00:00Z'); const add=(d,n)=>{const x=day(d);x.setUTCDate(x.getUTCDate()+n);return iso(x)}; const mon=d=>add(d,-((day(d).getUTCDay()+6)%7)); const dateText=(d,weekday=false)=>day(d).toLocaleDateString('en-US',{timeZone:'UTC',month:'short',day:'numeric',...(weekday?{weekday:'long'}:{})}); const today=()=>new Intl.DateTimeFormat('en-CA',{timeZone:CONFIG.profile?.timezone||'America/Los_Angeles',year:'numeric',month:'2-digit',day:'2-digit'}).format(new Date()); const uid=()=>Date.now().toString(36)+Math.random().toString(36).slice(2,8); const ALL_KINDS=['breakfast','lunch','dinner']; const KINDS=CONFIG.profile?.mealKinds?.filter(k=>ALL_KINDS.includes(k))||ALL_KINDS; const FIRST=mon(CONFIG.firstWeek||today()),KEY=CONFIG.storageKey||'weekly-meal-plan-v3',OLD=CONFIG.legacyStorageKey||KEY+'-legacy'; const choices=CONFIG.choices||{}; const defaultStore=CONFIG.store||{name:'',branch:'',zip:'',method:'Pickup',confirmed:false,url:null}; const profile={names:[],allergies:'',restrictions:'',preferences:'',breakfastLimit:20,lunchLimit:20,dinnerLimit:30,servings:{breakfast:1,lunch:1,dinner:1},budget:'',planner:'',shopper:'',timezone:'America/Los_Angeles',otherStores:'',...CONFIG.profile}; function blankMeal(date,kind,index=0,filled=false){return {id:uid(),date,kind,recipe:filled?(choices[kind]?.[index]||''):'',servings:profile.servings?.[kind]||1,cook:profile.names[(index+KINDS.indexOf(kind))%profile.names.length]||'',status:filled&&choices[kind]?.[index]?'planned':'empty',approval:'pending',repeat:false,comment:''}} function makeWeek(start,filled=false){return {manual:[],meals:Array.from({length:7},(_,i)=>KINDS.map(k=>blankMeal(add(start,i),k,i,filled))).flat()}} const seed={schema:2,rev:0,profile,store:defaultStore,weeks:{[FIRST]:makeWeek(FIRST,true)},stock:{},customItems:{},purchases:[],usage:[],requiresReview:false}; if(profile.members?.length)seed.weeks[FIRST]=window.Household.prepareWeek(seed.weeks[FIRST],profile.members,{scale:false}); let state=clone(seed),selected=FIRST,tab='meals',lastRaw=null,savable=false,pending=null,undo=[],editorId=null,quickItem=null,intakeRows=[],conflict=false,extraOpen=false,extraDraft='',openCookId=null; function meta(k,s=state){return ITEMS[k]||s.customItems[k]||{name:k,unit:'item',category:'Other'}} function options(a,current){return a.map(v=>`<option value="${esc(v)}" ${v===current?'selected':''}>${esc(v)}</option>`).join('')} function say(t){$('message').textContent=t} function valid(s){return s?.schema===2&&s.profile&&s.store&&s.weeks&&s.stock&&Array.isArray(s.usage)&&Array.isArray(s.purchases)&&Object.values(s.stock).every(v=>v.qty===null||Number.isFinite(v.qty)&&v.qty>=0)} function migrate(old){const next=clone(seed);next.profile={...profile,...old.profile};next.store={...defaultStore,...old.store};next.customItems=clone(old.customItems||{});next.weeks={};for(const[w,v]of Object.entries(old.weeks||{})){const week={manual:clone(v.manual||[]),meals:[]};for(const m of v.meals||[]){const index=Math.round((day(m.date)-day(w))/86400000);week.meals.push({...clone(m),servings:m.status==='cooked'?m.servings:(next.profile.servings?.[m.kind]||m.servings||1),cook:next.profile.names.includes(m.cook)?m.cook:next.profile.names[(index+KINDS.indexOf(m.kind))%next.profile.names.length]||'',approval:v.approved||m.status==='cooked'?'accepted':'pending',repeat:!!m.locked})}for(let i=0;i<7;i++)for(const kind of KINDS)if(!week.meals.some(m=>m.date===add(w,i)&&m.kind===kind))week.meals.push(blankMeal(add(w,i),kind,i,w===FIRST));next.weeks[w]=week}if(!next.weeks[FIRST])next.weeks[FIRST]=makeWeek(FIRST,true);for(const[k,v]of Object.entries(old.pantry||{}))next.stock[k]={qty:Number(v.qty)||0,checked:v.checked||today(),lowerBound:false};for(const r of old.receipts||[]){next.stock[r.item]??={qty:0,checked:today(),lowerBound:false};next.stock[r.item].qty=num(next.stock[r.item].qty+(r.remaining||0));next.purchases.push({...r,migrated:true})}next.requiresReview=!!old.requiresReview;return next} try{lastRaw=localStorage.getItem(KEY);if(lastRaw){const parsed=JSON.parse(lastRaw);if(valid(parsed))state=parsed}else{const old=JSON.parse(localStorage.getItem(OLD)||'null');if(old?.schema===1)state=migrate(old)}}catch(e){savable=false} // Initialize confirmed inputs even when an artifact sandbox blocks storage. if(!state.demoPantryInitialized){if(!Object.keys(state.stock).length){for(const[k,qty]of Object.entries(CONFIG.pantry||{}))state.stock[k]={qty,checked:today(),lowerBound:false,demo:!!CONFIG.demo}}state.demoPantryInitialized=true;state.rev++}for(const week of Object.values(state.weeks))for(let i=0;i<week.meals.length;i++){const m=week.meals[i];if(m.swap&&m.swap.status!=='blocked')week.meals[i]=window.MealSwaps.refresh(m,R,state.profile,week.meals,state.requiresReview,window.MealReplacements.replace)}selected=state.view?.week&&state.weeks[state.view.week]?state.view.week:FIRST;lastRaw=JSON.stringify(state);try{localStorage.setItem(KEY,lastRaw);savable=true}catch(e){savable=false} function ensure(s,w){if(s.weeks[w])return s.weeks[w];const result=makeWeek(w);const previous=Object.keys(s.weeks).filter(x=>x<w).sort().pop();if(previous){for(const src of s.weeks[previous].meals.filter(m=>m.repeat&&m.approval!=='rejected'&&m.recipe)){const i=Math.round((day(src.date)-day(previous))/86400000),target=result.meals.find(m=>m.date===add(w,i)&&m.kind===src.kind);if(target)Object.assign(target,{recipe:src.recipe,servings:src.servings,cook:src.cook,status:'planned',repeat:true})}}s.weeks[w]=s.profile.members?window.Household.prepareWeek(result,s.profile.members,{scale:!!s.profile.householdPortions}):result;return s.weeks[w]} function ingredients(m){return Object.fromEntries(Object.entries(R[m.recipe]?.ing||{}).map(([k,q])=>[k,num(q*m.servings/(R[m.recipe]?.yield||2))]))} function find(s,id){for(const[w,v]of Object.entries(s.weeks)){const m=v.meals.find(x=>x.id===id);if(m)return {w,m}}return null} function ledger(s){const pool={},reserved={},acceptedDemand={},weeks={};for(const[k,v]of Object.entries(s.stock))pool[k]=v.qty===null?0:v.qty;const work=[];for(const[w,v]of Object.entries(s.weeks)){weeks[w]={};for(const m of v.meals)if(m.status==='planned'&&m.approval!=='rejected'&&R[m.recipe])for(const[k,q]of Object.entries(ingredients(m)))work.push({w,k,q,accepted:m.approval==='accepted',date:m.date,kind:m.kind,label:dateText(m.date)+' '+m.kind,meal:m.id});for(const a of v.manual||[])work.push({w,k:a.id,q:a.qty,accepted:true,date:w,kind:'extra',label:'Extra · '+a.name})}work.sort((a,b)=>Number(b.accepted)-Number(a.accepted)||a.date.localeCompare(b.date)||KINDS.indexOf(a.kind)-KINDS.indexOf(b.kind));for(const x of work){const row=weeks[x.w][x.k]??={qty:0,covered:0,need:0,from:[],acceptedQty:0};const used=Math.min(pool[x.k]||0,x.q);pool[x.k]=num((pool[x.k]||0)-used);row.qty=num(row.qty+x.q);row.covered=num(row.covered+used);row.need=num(row.need+x.q-used);row.from.push({label:x.label,qty:x.q,accepted:x.accepted});if(x.accepted){row.acceptedQty=num(row.acceptedQty+x.q);acceptedDemand[x.k]=num((acceptedDemand[x.k]||0)+x.q);reserved[x.k]=num((reserved[x.k]||0)+used)}}return {weeks,reserved,acceptedDemand,pool}} function buyQty(k,row){const unit=meta(k).unit;return /can|egg|tortilla|pepper|onion|avocado|lime|cucumber|carrot|clove/.test(unit)?Math.ceil(row.need-1e-8):num(row.need)} function rows(){return Object.entries(ledger(state).weeks[selected]||{}).sort((a,b)=>(meta(a[0]).category+meta(a[0]).name).localeCompare(meta(b[0]).category+meta(b[0]).name))} function save(){if(!savable)return;state.view={week:selected};try{localStorage.setItem(KEY,JSON.stringify(state));lastRaw=JSON.stringify(state)}catch(e){savable=false;say('This browser cannot save changes right now. Keep this page open.')}} function checkConflict(){if(conflict)return true;if(savable)try{if(localStorage.getItem(KEY)!==lastRaw){conflict=true;$('conflict').hidden=false;return true}}catch(e){savable=false}return false} function commit(next,options={}){if(checkConflict())return say('Load the latest saved changes before editing.');if(options.recordUndo!==false){undo.push(clone(state));if(undo.length>20)undo.shift();}next.rev=(state.rev||0)+1;state=next;pending=null;$('review').hidden=true;quickItem=null;save();render();say('')} function preview(next,title){const before=ledger(state).weeks,after=ledger(next).weeks;const changes=[];for(const w of new Set([...Object.keys(before),...Object.keys(after)]))for(const k of new Set([...Object.keys(before[w]||{}),...Object.keys(after[w]||{})])){const a=before[w]?.[k]?.need||0,b=after[w]?.[k]?.need||0;if(Math.abs(a-b)>.001)changes.push(`${dateText(w)}: ${meta(k,next).name} — ${fmt(a)} → ${fmt(b)} ${meta(k,next).unit} to buy`)}pending=next;$('review').innerHTML=`<h3>${esc(title)}</h3>${changes.length?'<ul class="small">'+changes.slice(0,12).map(x=>'<li>'+esc(x)+'</li>').join('')+'</ul>':'<p class="small space-top">No grocery quantities change.</p>'}${changes.length>12?`<p class="small">And ${changes.length-12} more ingredient updates.</p>`:''}<div class="row space-top"><button class="primary" data-action="apply">Apply</button><button data-action="cancel">Cancel</button></div>`;$('review').hidden=false;$('review').scrollIntoView({block:'nearest'})} let mealScroll=0,householdDraft=null,householdScale=false; function closeEditor(){const returning=editorId; $('editor').hidden=true;editorId=null;root.classList.remove('recipe-view');pending=null;$('review').hidden=true;render();if(returning&&tab==='meals'){window.scrollTo(0,mealScroll);root.querySelector('[data-meal="'+returning+'"] [data-action="recipe"]')?.focus({preventScroll:true})}} function showCopy(title,text){$('copy-title').textContent=title;$('copy-text').value=text;$('copy-box').hidden=false;try{navigator.clipboard.writeText(text).then(()=>say('Copied. Paste it into this chat.')).catch(()=>say('Select and copy the text below.'))}catch(e){say('Select and copy the text below.')}} function readHouseholdForm(){return [...root.querySelectorAll('[data-member]')].map(row=>{const value=k=>row.querySelector('[data-person-field="'+k+'"]');return {id:row.dataset.member,name:value('name').value,group:value('group').value,age:value('age').value===''?null:Number(value('age').value),feeding:value('feeding').value,portion:value('portion')?(value('portion').value===''?null:Number(value('portion').value)):0,heat:value('heat')?.value||'usual',separate:value('separate').checked,needs:value('needs').value,meals:[...row.querySelectorAll('[data-person-meal]:checked')].map(input=>input.dataset.personMeal)}})} function redrawHousehold(){const box=root.querySelector('.household-settings');if(box)box.outerHTML=window.Household.settingsHTML(householdDraft||window.Household.initial(state.profile));if($('household-scale'))$('household-scale').checked=householdScale} function saveHousehold(){try{const members=window.Household.validate(readHouseholdForm()),next=clone(state);next.profile.members=members;const scaleCurrent=$('household-scale').checked;next.profile.householdPortions=true;next.weeks[selected]=window.Household.prepareWeek(next.weeks[selected],members,{scale:scaleCurrent});householdDraft=null;preview(next,'Save household and review this week');$('review').querySelector('h3').insertAdjacentHTML('afterend','<p class="small space-top">'+esc(members.map(m=>m.name).join(', '))+' · '+(scaleCurrent?'Update planned recipe quantities from the selected portions.':'Keep existing recipe quantities.')+' Planned meals need approval again. Cooked meals keep their history.</p>')}catch(e){say(e.message)}} function counts(){const m=state.weeks[selected].meals.filter(m=>m.recipe&&m.status==='planned');return {pending:m.filter(m=>m.approval==='pending').length,rejected:m.filter(m=>m.approval==='rejected').length,accepted:m.filter(m=>m.approval==='accepted').length}} function render(){ensure(state,selected);$('week-label').textContent=dateText(selected)+' – '+dateText(add(selected,6))+', '+selected.slice(0,4);$('week-date').value=selected;for(const k of ['meals','shopping','pantry','settings']){$(k).hidden=!!editorId||tab!==k;$('tab-'+k).setAttribute('aria-selected',String(tab===k))}renderMeals();renderShopping();renderPantry();renderSettings()} // A fixed two-line box: adapt type, never add rows to the card. function fitMealTitles(){for(const box of root.querySelectorAll('.meal-title')){if(!box.clientWidth)continue;const text=box.firstElementChild;const full=text.dataset.full||text.textContent;text.dataset.full=full;text.textContent=full;const css=getComputedStyle(box),max=parseFloat(css.getPropertyValue('--title-size'))||17;let size=max;box.style.fontSize=size+'px';const over=()=>text.scrollHeight>box.clientHeight+1||text.scrollWidth>box.clientWidth+1;while(over()&&size>11){size=Math.max(11,size-.5);box.style.fontSize=size+'px'}if(over()){let words=full.split(/\s+/);while(over()&&words.length>1){words.pop();text.textContent=words.join(' ')+'…'}}}} function renderMeals(){ const w=state.weeks[selected];let html=''; if(state.requiresReview)html+='<div class="notice">Dietary settings changed. Review these recipes before accepting meals.</div>'; for(let i=0;i<7;i++){ const d=add(selected,i);html+=`<div class="day"><h3 class="day-title">${esc(dateText(d,true))}</h3><div class="meal-grid" style="--meal-columns:${KINDS.length}">`; for(const kind of KINDS){ const m=w.meals.find(m=>m.date===d&&m.kind===kind),r=R[m?.recipe];if(!m)continue; const swap=m.swap,swapping=!!swap,active=m.status==='planned'||m.status==='cooked',done=m.status==='cooked',approval=done?'accepted':m.approval; const label=swap?(swap.status==='preparing'?'<span class="swap-icon" aria-hidden="true">↻</span> Swapping':swap.status==='ready'?'<span class="swap-icon" aria-hidden="true">↻</span> Swap ready':'Swap paused'):done?'Cooked':approval==='accepted'?'Accepted':approval==='rejected'?'Needs replacement':'Needs approval'; const unavailable=swapping||done,unapproved=approval!=='accepted'; html+=`<article class="meal ${kind} ${esc(approval)} ${swapping?'is-swapping':''}" data-meal="${esc(m.id)}" aria-busy="${swap?.status==='preparing'}"> ${active&&r?`<span class="ribbon ${approval==='pending'?'':esc(approval)} ${swap?.status==='preparing'?'swap-preparing':''}">${label}</span>`:''} <div class="row between"><span class="meal-kind">${kind}</span>${active&&r?`<div class="approval-buttons"><button data-action="accept" data-id="${esc(m.id)}" aria-label="Accept ${esc(r.name)}" title="Accept" aria-pressed="${approval==='accepted'}" aria-disabled="${unavailable}">👍</button><button data-action="reject" data-id="${esc(m.id)}" aria-label="Swap ${esc(r.name)}" title="Swap" aria-pressed="${approval==='rejected'}" aria-disabled="${unavailable}">👎</button></div>`:''}</div> <h3 class="meal-title" title="${active&&r?esc(r.name):''}" aria-label="${active&&r?esc(r.name):esc(m.status)}"><span>${active&&r?esc(cardNames[m.recipe]||r.cardName||r.name):esc(m.householdExcluded?'Separate food plan':{away:'Away',takeout:'Takeout',provided:'Meal provided',empty:'Choose a meal'}[m.status]||'Choose a meal')}</span></h3> <p class="meta">${active&&r?`<span class="meal-time">${r.total} min</span><span class="meta-divider" aria-hidden="true"> · </span><span class="meal-servings">${m.servings} servings</span>`:''}</p> <div class="cook-picker"><svg class="chef-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M7 16v5h10v-5M7 18h10M7 16V12a4 4 0 0 1-2-7 4 4 0 0 1 4 .1 4 4 0 0 1 7.6 0A4 4 0 1 1 17 12v4Z"/></svg><button class="cook-select" data-action="cook-open" data-id="${esc(m.id)}" role="combobox" aria-label="Cook for ${esc(dateText(m.date))} ${kind}" aria-haspopup="listbox" aria-controls="cook-options-${esc(m.id)}" aria-expanded="${openCookId===m.id}"><span>${esc(m.cook)}</span><span class="cook-chevron" aria-hidden="true">⌄</span></button>${openCookId===m.id?`<div class="cook-options" id="cook-options-${esc(m.id)}" role="listbox" aria-label="Choose cook">${state.profile.names.map(name=>`<button role="option" aria-selected="${name===m.cook}" data-action="cook-option" data-id="${esc(m.id)}" data-value="${esc(name)}">${esc(name)}</button>`).join('')}</div>`:''}</div> <div class="meal-actions"><button data-action="recipe" data-id="${esc(m.id)}" aria-disabled="${swapping}">Recipe</button>${r?`<button class="repeat ${m.repeat?'active':''}" data-action="repeat" data-id="${esc(m.id)}" aria-pressed="${m.repeat}" aria-disabled="${swapping||unapproved}">Repeat meal</button><button class="mark-cooked ${done?'done':''}" data-action="cooked" data-id="${esc(m.id)}" aria-pressed="${done}" aria-disabled="${swapping||unapproved||done}">${done?'✓ ':''}Mark cooked</button>`:''}</div> ${swap?`<div class="swap-help" role="status">${swap.status==='preparing'?'Finding a new meal…':swap.status==='ready'?`<button class="refresh-swap" data-action="refresh-swap" data-id="${esc(m.id)}">Refresh for your new recipe</button>`:esc(swap.reason||'Ask your AI for a compatible replacement.')}</div>`:m.replacementMessage?`<p class="replacement-help">${esc(m.replacementMessage)}</p>`:''} </article>`; }html+='</div></div>'; }$('meals').innerHTML=html;fitMealTitles(); } function extraItemsPanel(){const items=state.weeks[selected].manual;return `<section id="extra-items-panel" class="panel extras-panel" ${extraOpen?'':'hidden'} aria-label="Add extra items"><label for="extra-items-text">What else do you need?</label><p class="small muted">Type or use your keyboard’s microphone. Separate items with commas or new lines.</p><textarea id="extra-items-text" rows="4" maxlength="4000" placeholder="Coffee, 2 cartons of milk, paper towels&#10;Birthday candles">${esc(extraDraft)}</textarea><div class="row"><button class="primary" data-action="add-extras">Add to list</button><button data-action="close-extras">Close</button></div>${items.length?`<div class="extras-added"><h3>Extra items (${items.length})</h3>${items.map((m,i)=>`<div class="row between"><span>${esc(m.name)} · ${fmt(m.qty)} ${esc(m.unit)}</span><button data-action="remove-extra" data-id="${i}" aria-label="Remove ${esc(m.name)}">Remove</button></div>`).join('')}</div>`:''}</section>`} function parseExtras(text){const numbers={a:1,an:1,one:1,two:2,three:3,four:4,five:5,six:6,seven:7,eight:8,nine:9,ten:10,eleven:11,twelve:12};return text.split(/[\n,;]+/).map(t=>t.trim().replace(/^[-•]\s*/, '')).filter(Boolean).map(raw=>{let name=raw.replace(/^(?:please\s+)?(?:add|we need|i need)\s+/i,''),qty=1,unit='item';const m=name.match(/^(\d+(?:\.\d+)?|a|an|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)\s+(.+)$/i);if(m){qty=numbers[m[1].toLowerCase()]??Number(m[1]);name=m[2]}const u=name.match(/^(cartons?|bottles?|bags?|boxes?|packs?|packages?|cans?|rolls?|pounds?|lbs?|ounces?|oz|cups?|gallons?|liters?|litres?)\s+(?:of\s+)?(.+)$/i);if(u){const units={cartons:'carton',bottles:'bottle',bags:'bag',boxes:'box',packs:'pack',packages:'package',cans:'can',rolls:'roll',pounds:'lb',pound:'lb',lbs:'lb',ounces:'oz',ounce:'oz',cups:'cup',gallons:'gallon',liters:'liter',litres:'liter',litre:'liter'};unit=units[u[1].toLowerCase()]||u[1].toLowerCase();name=u[2]}else{const k=pantryKey(name);if(k&&/^(egg|tortilla|pepper|onion|avocado|lime|cucumber|carrot|clove|item)$/.test(meta(k).unit))unit=meta(k).unit}return {name:name.trim(),qty,unit}})} function renderShopping(){const c=counts();let html=`<div class="shop-heading"><div><h2>Shopping List</h2><p class="small muted">${esc(state.store.name)} · ${esc(state.store.method)}</p></div><div class="cart-tools"><button class="add-extras-button" data-action="toggle-extras" aria-expanded="${extraOpen}" aria-controls="extra-items-panel">Add extra items</button><button data-action="copy-list">Copy list</button><button class="primary" data-action="cart" aria-describedby="cart-status">Approve &amp; Add to Cart</button></div></div><p class="cart-note" id="cart-status">Review your list, then continue with your AI to add it to the store cart.</p>${extraItemsPanel()}`;let category='';const shoppingRows=rows();if(!shoppingRows.length)html+='<p class="empty">Add or accept meal ideas to build your list.</p>';for(const[k,r]of shoppingRows){const i=meta(k),stock=state.stock[k],qty=buyQty(k,r),uncertain=stock&&(stock.qty===null||stock.lowerBound&&r.need>0),covered=r.need<=0;if(category!==i.category){category=i.category;html+=`<h3 class="shop-group">${esc(category)}</h3>`}html+=`<div class="shop-item" data-item="${esc(k)}"><div class="shop-line"><button class="check-button ${covered?'checked':''}" data-action="bought" data-id="${esc(k)}" aria-label="${covered?'Covered':'Mark purchased'}: ${esc(i.name)}" ${covered?'aria-disabled="true"':''}>${covered?'✓':''}</button><div><div class="shop-name">${esc(i.name)}</div><div class="shop-qty">${covered?'In My Pantry':uncertain?'Check pantry · recipe shortfall '+fmt(r.need)+' '+esc(i.unit):fmt(qty)+' '+esc(i.unit)}${qty>r.need&&!covered?' · '+fmt(qty-r.need)+' extra':''}</div></div><button class="pantry-shortcut" data-action="owned" data-id="${esc(k)}">${covered?'View pantry':'In My Pantry'}</button></div>${quickItem===k?`<div class="quick-pantry"><p>This week needs ${fmt(r.qty)} ${esc(i.unit)} ${esc(i.name.toLowerCase())}. Do you have at least that much on hand?</p><div class="mini-actions"><button data-action="enough" data-id="${esc(k)}">Yes, enough</button><button data-action="unknown" data-id="${esc(k)}">I’m not sure how much</button><button data-action="pantry-edit" data-id="${esc(k)}">Enter amount</button></div></div>`:''}</div>`}$('shopping').innerHTML=html;renderCartResults()} function renderPantry(){const l=ledger(state);let html=`<div class="section-bar"><h2>Digital Pantry</h2><button data-action="add-pantry">Add item</button></div><p class="small muted pantry-intro">Accepted meals reserve ingredients. Mark a meal cooked to subtract what you used.</p><details ${Object.keys(state.stock).length?'':'open'}><summary>Add a pantry list</summary><p class="small muted space-top">Type or use keyboard dictation. Put one item per line; you can check amounts before saving.</p><label class="space-top">What do you already have?<textarea id="pantry-intake" placeholder="12 eggs&#10;Rice — half a bag&#10;Salsa&#10;2 avocados"></textarea></label><button class="space-top" data-action="review-intake">Review items</button></details><div id="intake-review"></div><datalist id="ingredient-names">${Object.values({...ITEMS,...state.customItems}).map(i=>'<option>'+esc(i.name)+'</option>').join('')}</datalist>`;const keys=Object.keys(state.stock).sort((a,b)=>meta(a).name.localeCompare(meta(b).name));if(!keys.length)html+='<p class="empty">Your pantry is ready to fill. You can also tap “In My Pantry” on the shopping list.</p>';else html+='<div class="table-wrap"><table class="pantry-table"><thead><tr><th>Item</th><th>On hand</th><th>Reserved</th><th>After meals</th><th></th></tr></thead><tbody>'+keys.map(k=>{const p=state.stock[k],i=meta(k),used=l.reserved[k]||0,need=l.acceptedDemand[k]||0,low=p.qty!==null&&need>p.qty,unknown=p.qty===null||p.lowerBound&&low;return `<tr><td>${esc(i.name)}<div class="stock-status ${low||unknown?'low':''}">${unknown?'Check amount':low?'Restock '+fmt(need-p.qty)+' '+esc(i.unit):p.qty===0?'Out of stock':p.demo?'Demo stock':p.lowerBound?'At least this much confirmed':'Updated '+esc(p.checked||'today')}</div></td><td data-label="On hand">${p.qty===null?'Unknown':(p.lowerBound?'≥ ':'')+fmt(p.qty)}</td><td data-label="Reserved">${fmt(used)}</td><td data-label="After meals">${p.qty===null?'?':fmt(Math.max(0,p.qty-used))}</td><td><button data-action="pantry-edit" data-id="${esc(k)}">Edit</button></td></tr>`}).join('')+'</tbody></table></div>';html+=`<details class="disclosure"><summary>Recent pantry activity</summary>${state.usage.slice(-12).reverse().map(u=>`<p>${esc(u.date)} · ${esc(u.label)} · ${esc(u.note)}</p>`).join('')||'<p>No meals marked cooked yet.</p>'}</details>`;$('pantry').innerHTML=html;if(intakeRows.length)renderIntake()} function scheduleSettings(){return window.MealSchedule.html(state,esc,options)} function saveSchedule(copy){try{const next=clone(state);next.schedulePreferences=window.MealSchedule.validate({draftDay:$('schedule-draft-day').value,draftTime:$('schedule-draft-time').value,reviewDay:$('schedule-review-day').value,reviewTime:$('schedule-review-time').value,timezone:$('schedule-timezone').value.trim(),owner:$('schedule-owner').value,shoppingDay:$('schedule-shopping-day').value,pantryCheck:$('schedule-pantry-check').checked});if(checkConflict())return;commit(next);if(copy)showCopy('Set up weekly planning — paste into your AI chat',window.MealSchedule.request(next.schedulePreferences))}catch(e){say(e.message)}} function renderSettings(){const p=state.profile,st=state.store;$('settings').innerHTML=`<h2>Settings</h2>${window.Household.settingsHTML(householdDraft||window.Household.initial(p))}<section class="settings-section"><h3 class="space-top">Preferences &amp; stores</h3><div class="fields"><label>Allergies<input id="set-allergies" value="${esc(p.allergies)}"></label><label>Dietary requirements<input id="set-restrictions" value="${esc(p.restrictions)}"></label><label class="full">Preferences<input id="set-preferences" value="${esc(p.preferences)}"></label><label>Breakfast cooking time<input id="set-breakfast" type="number" min="1" max="180" value="${p.breakfastLimit}"></label><label>Lunch cooking time<input id="set-lunch" type="number" min="1" max="180" value="${p.lunchLimit||20}"></label><label>Dinner cooking time<input id="set-dinner" type="number" min="1" max="180" value="${p.dinnerLimit}"></label><label>Weekly grocery budget<input id="set-budget" value="${esc(p.budget)}" placeholder="Optional"></label><label>Store<input id="set-store" value="${esc(st.name)}"></label><label class="full">Branch<input id="set-branch" value="${esc(st.branch)}"></label><label>ZIP code<input id="set-zip" value="${esc(st.zip)}"></label><label>Shopping method<select id="set-method">${options(['Pickup','Delivery','In-store'],st.method)}</select></label></div><p class="small muted">${p.otherStores?'Also used: '+esc(p.otherStores):''}</p><button class="primary space-top" data-action="settings-save">Save settings</button></section><section class="settings-section"><h3>Cart workflow</h3><p class="small space-top">${esc(st.name)} · ${esc(st.method)} · ${esc(st.branch)}</p><p class="small muted space-top">Prepare an approved list for your AI chat. It checks sign-in, matches products, asks for product approval, then verifies additions and reports missing items. Checkout stays with you.</p><button class="space-top" data-action="cart-help">Prepare cart with my AI</button></section>${scheduleSettings()}<div class="row"><button data-action="undo" ${undo.length?'':'aria-disabled="true"'}>Undo last change</button></div><p class="small muted">${savable?'Changes are saved in this browser.':'Saving is unavailable in this browser.'} They do not sync between devices.</p>`} function recipeEditor(id){const f=find(state,id);if(!f)return;if(!editorId)mealScroll=window.scrollY;editorId=id;root.classList.add('recipe-view');for(const k of ['meals','shopping','pantry','settings'])$(k).hidden=true;const m=f.m,r=R[m.recipe];$('editor').innerHTML=`<div class="row between"><button class="back-schedule" data-action="close-editor">← Back to meal schedule</button><span class="small muted">${esc(dateText(m.date))} · ${esc(m.kind)}</span></div><div class="fields"><label>Recipe<select id="edit-recipe"><option value="">Choose a recipe</option>${window.Household.recipeOptions(R,m.familyMembers||window.Household.participants(state.profile.members||[],m.kind),m.recipe)}</select></label><label>Servings<input id="edit-servings" type="number" value="${m.servings}" min="0" max="12" step="0.25"></label><label>Cook<select id="edit-cook">${options(state.profile.names,m.cook)}</select></label><label>Status<select id="edit-status">${options(['planned','away','takeout','provided','empty'],m.status==='cooked'?'planned':m.status)}</select></label><label>Date<input id="edit-date" type="date" value="${m.date}"></label><label>Meal<select id="edit-kind">${options(KINDS,m.kind)}</select></label><label class="full">Notes<textarea id="edit-note">${esc(m.comment)}</textarea></label></div>${m.status==='cooked'?'<p class="small">This meal is already cooked. Its ingredients and date stay in the pantry history.</p>':'<button class="primary" data-action="save-meal">Review changes</button>'}${r?`<div class="recipe-content"><h3>${esc(r.name)}</h3>${r.image?`<figure class="recipe-hero"><img src="${esc(r.image.src)}" alt="${esc(r.image.alt)}" width="1536" height="1024"><figcaption>${esc(r.image.caption)}</figcaption></figure>`:''}<p class="small muted">${r.active} min active · ${r.total} min total · ${m.servings} servings</p>${window.Household.recipeHTML(m,state.profile,r)}<div class="recipe-columns"><ul class="recipe-list">${Object.entries(ingredients(m)).map(([k,q])=>`<li>${fmt(q)} ${esc(meta(k).unit)} ${esc(meta(k).name)}</li>`).join('')}</ul><ol class="recipe-list">${r.steps.map(t=>'<li>'+esc(t.replace(/two bowls/g,'bowls').replace(/each tortilla/g,'each tortilla'))+'</li>').join('')}</ol></div></div>`:''}`;$('editor').hidden=false;const recipe=$('editor').querySelector('.recipe-content');if(recipe)$('editor').insertBefore(recipe,$('editor').querySelector('.fields'));$('editor').setAttribute('tabindex','-1');$('editor').focus({preventScroll:true});window.scrollTo(0,0)} function saveMeal(){const f=find(state,editorId);if(!f||f.m.status==='cooked')return;const next=clone(state),{m,w}=find(next,editorId),date=$('edit-date').value,kind=$('edit-kind').value,servings=Number($('edit-servings').value),recipe=$('edit-recipe').value,status=$('edit-status').value;if(!date||isNaN(+day(date))||!Number.isFinite(servings)||servings<0||status==='planned'&&servings<0.25||servings>12)return say('Choose a valid date and 0.25–12 recipe servings.');if(status==='planned'&&!R[recipe])return say('Choose a recipe first.');const dest=mon(date);ensure(next,dest);const other=next.weeks[dest].meals.find(x=>x.date===date&&x.kind===kind&&x.id!==m.id);if(other?.status==='cooked')return say('Choose a slot that does not already contain a cooked meal.');if(other){next.weeks[dest].meals=next.weeks[dest].meals.filter(x=>x.id!==other.id);other.date=m.date;other.kind=m.kind;other.approval='pending';next.weeks[w].meals.push(other)}next.weeks[w].meals=next.weeks[w].meals.filter(x=>x.id!==m.id);const changed=m.recipe!==recipe||m.servings!==servings||m.date!==date||m.kind!==kind||m.status!==status;Object.assign(m,{date,kind,recipe,status,servings,cook:$('edit-cook').value,comment:$('edit-note').value,approval:changed?'pending':m.approval});if(changed)delete m.swap;if(next.profile.members&&changed)m.familyMembers=clone(window.Household.participants(next.profile.members,kind));next.weeks[dest].meals.push(m);preview(next,'Update this meal')} function pantryKey(name){const v=name.trim().toLowerCase();return Object.keys({...ITEMS,...state.customItems}).find(k=>meta(k).name.toLowerCase()===v)||Object.keys(aliases).find(k=>aliases[k].includes(v))} function pantryAmountLabel(k){if(!k)return 'On hand';const u=meta(k).unit;return /^(egg|tortilla|pepper|onion|avocado|lime|cucumber|carrot|clove|item)$/.test(u)?'On hand':'On hand ('+u+')'} function pantryEditor(k=''){const p=state.stock[k]||{qty:null,checked:today()},i=meta(k);$('editor').innerHTML=`<div class="row between"><h2>${k?'Update pantry item':'Add to My Pantry'}</h2><button data-action="close-editor">Close</button></div><div class="fields"><label class="full">Item<input id="pantry-name" list="ingredient-names" value="${k?esc(i.name):''}" ${k?'readonly':''}></label><label><span id="pantry-amount-label">${esc(pantryAmountLabel(k))}</span><input id="pantry-qty" type="number" min="0" max="100000" step="any" value="${p.qty===null?'':p.qty}" placeholder="Unknown"></label></div><p class="small muted">Leave the amount blank if you’re unsure. Confirmed quantities replace the current amount; they don’t add to it.</p><div class="row space-top"><button class="primary" data-action="pantry-save" data-id="${esc(k)}">Save pantry item</button>${k?`<button data-action="pantry-empty" data-id="${esc(k)}">Mark empty</button>`:''}</div>`;$('editor').hidden=false;$('editor').scrollIntoView({block:'start'})} const aliases={eggs:['egg','eggs'],rice:['rice','quick-cooking rice','dry rice'],beans:['black beans','beans'],avocado:['avocados','avocado'],tortilla:['flour tortillas','tortillas'],salsa:['salsa','spicy salsa'],cheddar:['cheddar','shredded cheddar','cheese'],yogurt:['yogurt','greek yogurt'],chicken:['chicken','chicken breast'],oil:['oil','cooking oil'],onion:['onion','onions'],pepper:['bell pepper','bell peppers'],potato:['potato','potatoes'],lime:['lime','limes'],chickpeas:['chickpeas','chickpea'],hot:['hot sauce']}; function resolveItem(name,unit,s=state){let k=Object.keys({...ITEMS,...s.customItems}).find(k=>meta(k,s).name.toLowerCase()===name.trim().toLowerCase()&&meta(k,s).unit.toLowerCase()===unit.trim().toLowerCase());if(!k){k=Object.keys(aliases).find(k=>ITEMS[k]&&aliases[k].includes(name.trim().toLowerCase())&&ITEMS[k].unit.toLowerCase()===unit.trim().toLowerCase())}return k} function createItem(s,name,unit){const existing=resolveItem(name,unit,s);if(existing)return existing;const k='custom-'+uid();s.customItems[k]={name,unit,category:'Other'};return k} function parseIntake(text){return text.split(/[\n,;]+/).map(t=>t.trim()).filter(Boolean).slice(0,60).map(t=>{const lower=t.toLowerCase();const k=Object.keys({...ITEMS,...state.customItems}).find(k=>meta(k).name.toLowerCase()===lower)||Object.keys(aliases).find(k=>aliases[k].some(a=>new RegExp('(^|\\s)'+a.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')+'(?=\\s|$|[—–-])').test(lower)));if(!k)return {key:'',name:t,qty:'',unit:'item',original:t};const i=meta(k);let q='',match=t.match(/^\s*(\d+(?:\.\d+)?)\s+/);if(match){const remainder=t.slice(match[0].length).toLowerCase();const names=aliases[k]||[i.name.toLowerCase()];if(names.includes(remainder)&&['egg','avocado','pepper','onion','lime','tortilla','cucumber','carrot','clove'].includes(i.unit))q=Number(match[1]);else if(remainder.startsWith(i.unit+' '))q=Number(match[1])}return {key:k,name:i.name,qty:q,unit:i.unit,original:t}})} function renderIntake(){$('intake-review').innerHTML=`<div class="panel"><h3>Check your pantry list</h3><p class="small muted">Unknown amounts stay blank. Saving replaces any existing amount for the same item.</p>${intakeRows.map((r,i)=>`<div class="review-item" data-intake="${i}" data-unit="${esc(r.unit)}"><label>Item<input data-field="name" value="${esc(r.name)}"></label><label>Amount<input data-field="qty" type="number" min="0" step="any" value="${r.qty}"></label><p class="small muted">${esc(r.unit)}</p><button data-action="intake-remove" data-id="${i}" aria-label="Remove ${esc(r.name)}">×</button><p class="original">You said: ${esc(r.original)}</p></div>`).join('')}<div class="row space-top"><button class="primary" data-action="intake-save">Save to My Pantry</button><button data-action="intake-cancel">Cancel</button></div></div>`} function shoppingText(){return ['Shopping list · '+selected,state.store.name+' · '+state.store.method+' · '+state.store.branch,...rows().filter(([k,r])=>r.need>0).map(([k,r])=>`${state.stock[k]?.qty===null?'Check pantry: ':'□ '}${meta(k).name} — ${fmt(buyQty(k,r))} ${meta(k).unit}`)].join('\n')} function cookMeal(id){const f=find(state,id);if(!f||f.m.status!=='planned'||f.m.approval!=='accepted')return;const next=clone(state),m=find(next,id).m;if(next.usage.some(x=>x.mealId===id))return say('This meal’s pantry use is already recorded.');const shortages=[];for(const[k,q]of Object.entries(ingredients(m))){const p=next.stock[k];if(!p||p.qty===null){shortages.push(meta(k,next).name);continue}const used=Math.min(p.qty,q);p.qty=num(p.qty-used);p.checked=today();if(used<q)shortages.push(meta(k,next).name)}m.status='cooked';m.cookedAt=today();next.usage.push({id:uid(),mealId:id,date:today(),label:R[m.recipe].name,ingredients:ingredients(m),note:shortages.length?'Deducted recorded stock; confirm amounts for '+shortages.join(', '):'Ingredients deducted from My Pantry'});const y=window.scrollY;commit(next);window.scrollTo(0,y);root.querySelector('[data-action="cooked"][data-id="'+id+'"]')?.focus({preventScroll:true})} root.addEventListener('input',e=>{if(e.target.id==='pantry-name')$('pantry-amount-label').textContent=pantryAmountLabel(pantryKey(e.target.value));if(e.target.id==='extra-items-text')extraDraft=e.target.value}); function unavailableHelp(action,id){const box=$('action-help');let title,body;if(action==='cart'){title='Review & add to '+esc(state.store.name)+' cart';body='<p>Send this shopping request to your AI to review product matches, then add approved items on the store website.</p><ol><li>Accept the meals you want and check My Pantry.</li><li>Choose <strong>Copy cart request</strong>, then paste it into this AI chat.</li><li>Review the product matches and prices. If your AI supports browser actions and you are signed in, it can add approved products and report anything missing. Otherwise, use the list in your store’s website or app.</li></ol><p class="small">'+(state.store.name.trim().toLowerCase()==='safeway'?'Use Safeway directly for your chosen pickup or delivery option. Sign-in is checked in your AI session; no Instacart account is required.':'Ask your AI to check the selected store’s supported shopping route.')+'</p><div class="row"><button class="primary" data-action="cart-handoff">Copy cart request</button>'+(state.store.name.trim().toLowerCase()==='safeway'?'<a href="https://www.safeway.com/erums/cart" target="_blank" rel="noopener noreferrer">View Safeway cart</a>':'')+'</div>'}else if(['repeat','cooked','accept','reject','recipe'].includes(action)){const m=find(state,id)?.m;title=m?.swap?'Meal swap':m?.status==='cooked'?'Already marked cooked':'Accept this meal first';body=m?.swap?'<p>'+esc(m.swap.status==='preparing'?'Finding a new meal. The Swap indicator will say ready when you can refresh.':m.swap.status==='ready'?'Your replacement is ready. Refresh this page or use the button below to load it in the same slot.':m.swap.reason)+'</p>'+(m.swap.status==='ready'?'<button data-action="refresh-swap" data-id="'+esc(id)+'">Refresh meal</button>':''):m?.status==='cooked'?'<p>Its pantry use is already recorded. This action will not subtract it twice.</p>':'<p>Tap thumbs-up to accept this recipe. Then Repeat meal and Mark cooked become available.</p>'}else if(action==='bought'){title='Already covered by your pantry';body='<p>No additional '+esc(meta(id).name.toLowerCase())+' is needed for this plan. Choose View pantry to correct the amount if your stock has changed.</p>'}else{title='Nothing to undo yet';body='<p>Make a change to a meal or pantry item. Then return here to undo the latest change from this session.</p>'}box.innerHTML='<div class="row between"><h2>'+title+'</h2><button data-action="close-help" aria-label="Close explanation">Close</button></div>'+body;box.hidden=false;box.focus({preventScroll:true});box.scrollIntoView({block:'nearest'})} root.addEventListener('click',e=>{const b=e.target.closest('button');if(!b){const card=e.target.closest('[data-meal]');if(card&&!e.target.closest('select,input,label,textarea,a')){if(find(state,card.dataset.meal)?.m.swap)unavailableHelp('recipe',card.dataset.meal);else recipeEditor(card.dataset.meal)};return;}if(b.dataset.tab){$('action-help').hidden=true;tab=b.dataset.tab;openCookId=null;closeEditor();render();window.scrollTo(0,0);say('');return}const a=b.dataset.action,id=b.dataset.id;if(!a)return;if(a==='close-help')return $('action-help').hidden=true;if(b.getAttribute('aria-disabled')==='true')return unavailableHelp(a,id); if(a==='prev'||a==='next'){if(checkConflict())return;selected=add(selected,a==='prev'?-7:7);ensure(state,selected);pending=null;$('review').hidden=true;closeEditor();render();save();return} if(a==='cart')return unavailableHelp('cart'); if(['accept','reject','repeat','recipe','cooked'].includes(a)&&find(state,id)?.m.swap)return unavailableHelp(a,id); if(a==='cook-open'){openCookId=openCookId===id?null:id;renderMeals();if(openCookId){const list=$('cook-options-'+id);(list.querySelector('[aria-selected="true"]')||list.querySelector('[role="option"]'))?.focus({preventScroll:true})}else root.querySelector('[data-action="cook-open"][data-id="'+id+'"]')?.focus({preventScroll:true});return} if(a==='cook-option'){const value=b.dataset.value;if(!state.profile.names.includes(value)||checkConflict())return;const next=clone(state),f=find(next,id);if(!f)return;f.m.cook=value;openCookId=null;commit(next);root.querySelector('[data-action="cook-open"][data-id="'+id+'"]')?.focus({preventScroll:true});return} if(a==='recipe')return recipeEditor(id);if(a==='close-editor')return closeEditor();if(a==='close-copy')return $('copy-box').hidden=true;if(a==='save-meal')return saveMeal(); if(a==='apply'){if(!pending)return;const next=pending;commit(next,'Changes saved.');closeEditor();return}if(a==='cancel'){pending=null;$('review').hidden=true;return} if(a==='undo'){if(!undo.length||checkConflict())return;state=undo.pop();state.rev++;pending=null;$('review').hidden=true;closeEditor();save();render();return say('Last change undone.')} if(a==='reload'){try{const raw=localStorage.getItem(KEY),s=JSON.parse(raw);if(!valid(s))throw Error();state=s;lastRaw=raw;conflict=false;undo=[];pending=null;$('conflict').hidden=true;$('review').hidden=true;closeEditor();render();say('Latest changes loaded.')}catch(e){say('Could not load saved data.')}return} if(a==='refresh-swap'){if(checkConflict())return;const next=clone(state),f=find(next,id);if(!f?.m.swap)return;Object.assign(f.m,window.MealSwaps.refresh(f.m,R,next.profile,next.weeks[f.w].meals,next.requiresReview,window.MealReplacements.replace));if(f.m.swap?.status!=='blocked')delete f.m.swap;const y=window.scrollY;commit(next);window.scrollTo(0,y);return} if(a==='accept'||a==='reject'){ if(checkConflict())return;if(a==='accept'&&state.requiresReview)return say('Review the new dietary requirements first.'); const next=clone(state),f=find(next,id);if(!f||f.m.status!=='planned'||f.m.swap)return; if(a==='reject'){ Object.assign(f.m,window.MealSwaps.begin(f.m));commit(next); setTimeout(()=>{if(checkConflict())return;const fresh=clone(state),current=find(fresh,id);if(!current?.m.swap||current.m.swap.status!=='preparing')return;Object.assign(current.m,window.MealSwaps.prepare(current.m,R,fresh.profile,fresh.weeks[current.w].meals,fresh.requiresReview,window.MealReplacements.replace));commit(fresh,{recordUndo:false})},350); return; } f.m.approval='accepted';delete f.m.replacementMessage;return commit(next); } if(a==='repeat'){const next=clone(state),f=find(next,id);if(!f||f.m.approval!=='accepted'||f.m.swap)return;f.m.repeat=!f.m.repeat;return commit(next,f.m.repeat?'This meal will repeat when you open a new future week.':'Repeat turned off. Existing weeks stay unchanged.')} if(a==='cooked')return cookMeal(id); if(a==='copy-list')return showCopy('Shopping list',shoppingText()); if(a==='cart-handoff'){if(checkConflict())return say('Load the latest saved changes before preparing a cart request.');try{const request=window.CartHandoff.prepare(state,selected,{ledger,meta,buyQty});showCopy('Cart request — paste into your AI chat',window.CartHandoff.text(request));$('copy-box').scrollIntoView({block:'nearest'});$('copy-text').focus();$('copy-text').select()}catch(error){say(error.message)}return} if(a==='cart-help')return unavailableHelp('cart'); if(a==='owned'){const row=ledger(state).weeks[selected]?.[id];if(row?.need<=0)return pantryEditor(id);quickItem=quickItem===id?null:id;renderShopping();return} if(a==='enough'){const row=ledger(state).weeks[selected]?.[id];if(!row)return;const next=clone(state),p=next.stock[id]||{qty:0};next.stock[id]={qty:num(Math.max(p.qty||0,row.qty)),checked:today(),lowerBound:true};return commit(next,'Minimum pantry amount recorded. Stock is shared across planned weeks.')} if(a==='unknown'){const next=clone(state);next.stock[id]={qty:null,checked:today(),lowerBound:false};return commit(next,'Added to My Pantry. Check the amount before shopping.')} if(a==='pantry-edit')return pantryEditor(id);if(a==='add-pantry')return pantryEditor(); if(a==='pantry-empty'){const next=clone(state);next.stock[id]={qty:0,checked:today(),lowerBound:false};commit(next,'Marked empty. Shopping needs updated.');closeEditor();return} if(a==='pantry-save'){const name=$('pantry-name').value.trim(),unit=meta(id||pantryKey(name)).unit,raw=$('pantry-qty').value,qty=raw===''?null:Number(raw);if(!name||!unit||qty!==null&&(!Number.isFinite(qty)||qty<0||qty>100000))return say('Enter an item and a valid amount, or leave the amount unknown.');const next=clone(state),k=id||createItem(next,name,unit);next.stock[k]={qty,checked:today(),lowerBound:false};commit(next,'Pantry updated. Shopping list recalculated.');closeEditor();return} if(a==='review-intake'){const text=$('pantry-intake').value.trim();if(!text)return say('List a few things you already have.');intakeRows=parseIntake(text);renderIntake();return} if(a==='intake-remove'){intakeRows.splice(Number(id),1);renderIntake();return}if(a==='intake-cancel'){intakeRows=[];renderPantry();return} if(a==='intake-save'){const next=clone(state),seen=new Set();for(const row of root.querySelectorAll('[data-intake]')){const name=row.querySelector('[data-field=name]').value.trim(),unit=pantryKey(name)?meta(pantryKey(name)).unit:row.dataset.unit,raw=row.querySelector('[data-field=qty]').value,qty=raw===''?null:Number(raw);if(!name||!unit||qty!==null&&(!Number.isFinite(qty)||qty<0||qty>100000))return say('Check each item, amount and unit.');const k=createItem(next,name,unit);if(seen.has(k))return say('Combine duplicate entries for '+name+' before saving.');seen.add(k);next.stock[k]={qty,checked:today(),lowerBound:false}}intakeRows=[];return commit(next,'Pantry list saved. Confirmed stock now reduces the shopping list.')} if(a==='bought'){const row=ledger(state).weeks[selected]?.[id];if(!row||row.need<=0)return;const next=clone(state),q=buyQty(id,row),old=next.stock[id]||{qty:0,lowerBound:false};next.stock[id]={qty:num((old.qty||0)+q),checked:today(),lowerBound:old.qty===null||old.lowerBound};next.purchases.push({id:uid(),item:id,qty:q,date:today(),week:selected,store:state.store.name});return commit(next,'Marked purchased and added to My Pantry.')} if(a==='toggle-extras'||a==='close-extras'){extraOpen=a==='toggle-extras'?!extraOpen:false;renderShopping();if(extraOpen)$('extra-items-text').focus();else root.querySelector('[data-action="toggle-extras"]').focus();return} if(a==='add-extras'){const text=$('extra-items-text').value.trim();if(!text)return say('Type or dictate the extra items you need.');const items=parseExtras(text);if(items.length>60)return say('Add up to 60 items at a time.');if(items.some(i=>!i.name||!Number.isFinite(i.qty)||i.qty<=0||i.qty>100000))return say('Check the quantities; use positive numbers.');if(checkConflict())return say('Load the latest saved changes before adding items.');const next=clone(state);for(const item of items){const k=createItem(next,item.name,item.unit);next.weeks[selected].manual.push({id:k,...item})}extraDraft='';extraOpen=true;commit(next);$('extra-items-text').focus();return} if(a==='remove-extra'){const next=clone(state);next.weeks[selected].manual.splice(Number(id),1);return commit(next,'Extra removed; pantry quantities kept.')} if(a==='schedule-save'||a==='schedule-copy')return saveSchedule(a==='schedule-copy'); if(a==='settings-save'){const next=clone(state),p=next.profile,allergies=$('set-allergies').value.trim(),restrictions=$('set-restrictions').value.trim();if(!allergies||!restrictions)return say('Enter allergies and restrictions, or “None.”');if(allergies!==p.allergies||restrictions!==p.restrictions){next.requiresReview=true;for(const w of Object.values(next.weeks))for(const m of w.meals)if(m.status==='planned')m.approval='pending'}Object.assign(p,{allergies,restrictions,preferences:$('set-preferences').value,budget:$('set-budget').value,breakfastLimit:Number($('set-breakfast').value)||20,lunchLimit:Number($('set-lunch').value)||20,dinnerLimit:Number($('set-dinner').value)||30});Object.assign(next.store,{name:$('set-store').value.trim(),branch:$('set-branch').value.trim(),zip:$('set-zip').value.trim(),method:$('set-method').value});next.store.confirmed=false;next.store.url=next.store.name==='Safeway'&&next.store.branch===defaultStore.branch?defaultStore.url:null;return commit(next,'Settings updated.')} }); document.addEventListener('click',e=>{if(openCookId&&!e.target.closest('.cook-picker')){openCookId=null;renderMeals()}}); root.addEventListener('keydown',e=>{const picker=e.target.closest('.cook-picker');if(!picker)return;const trigger=picker.querySelector('[data-action="cook-open"]'),list=picker.querySelector('[role="listbox"]');if(e.key==='Escape'&&list){e.preventDefault();const id=openCookId;openCookId=null;renderMeals();root.querySelector('[data-action="cook-open"][data-id="'+id+'"]')?.focus({preventScroll:true});return}if(['ArrowDown','ArrowUp','Home','End'].includes(e.key)){e.preventDefault();if(!list){trigger.click();return}const options=[...list.querySelectorAll('[role="option"]')],current=options.indexOf(document.activeElement),index=e.key==='Home'?0:e.key==='End'?options.length-1:(current+(e.key==='ArrowDown'?1:-1)+options.length)%options.length;options[index]?.focus({preventScroll:true})}if(e.key==='Tab'&&list){setTimeout(()=>{if(!root.querySelector('.cook-picker:focus-within')&&openCookId){openCookId=null;renderMeals()}},0)}}); root.addEventListener('change',e=>{const select=e.target.closest('select[data-cook]');if(!select)return;const id=select.dataset.cook,value=select.value;if(!state.profile.names.includes(value))return;if(checkConflict()){select.value=find(state,id)?.m.cook||'';return say('Load the latest saved changes before editing.')}const next=clone(state),f=find(next,id);if(!f||f.m.cook===value)return;f.m.cook=value;commit(next);root.querySelector('select[data-cook="'+id+'"]')?.focus({preventScroll:true})}); root.addEventListener('input',e=>{if(e.target.closest('[data-member]'))householdDraft=readHouseholdForm()}); root.addEventListener('change',e=>{if(e.target.id==='household-scale'){householdScale=e.target.checked;return}if(!e.target.matches('[data-person-field="group"],[data-person-field="feeding"]'))return;householdDraft=readHouseholdForm();const member=householdDraft.find(m=>m.id===e.target.closest('[data-member]').dataset.member);if(e.target.dataset.personField==='group'){member.age=null;member.feeding=member.group==='baby'?'unknown':'family';member.portion=member.group==='adult'?1:null}if(member.feeding!=='family')member.portion=0;else if(!member.portion)member.portion=null;householdScale=$('household-scale').checked;const field=e.target.dataset.personField;redrawHousehold();root.querySelector('[data-member="'+member.id+'"] [data-person-field="'+field+'"]')?.focus()}); root.addEventListener('click',e=>{const button=e.target.closest('button'),action=button?.dataset.action;if(!['member-add','member-remove','household-save','household-copy'].includes(action))return;if(action==='household-save')return saveHousehold();if(action==='household-copy'){try{showCopy('Household for meal planning',window.Household.brief(window.Household.validate(readHouseholdForm())))}catch(error){say(error.message)}return}householdDraft=readHouseholdForm();householdScale=$('household-scale').checked;if(action==='member-add'){if(householdDraft.length>=20)return say('This planner supports up to 20 household members.');householdDraft.push(window.Household.blank('member-'+uid()));redrawHousehold();root.querySelector('.member-card:last-child input')?.focus()}else{householdDraft=householdDraft.filter(m=>m.id!==button.dataset.id);redrawHousehold();root.querySelector('[data-action="member-add"]')?.focus()}}); $('week-date').addEventListener('change',()=>{if(!$('week-date').value||checkConflict())return;selected=mon($('week-date').value);ensure(state,selected);pending=null;$('review').hidden=true;closeEditor();render();save()}); window.addEventListener('resize',fitMealTitles); if(window.ResizeObserver)new ResizeObserver(fitMealTitles).observe(root); if(document.fonts?.ready)document.fonts.ready.then(fitMealTitles); window.addEventListener('storage',e=>{if(e.key===KEY&&e.newValue!==lastRaw){conflict=true;$('conflict').hidden=false}}); // Independent of meal Undo: a cart receipt is evidence, not a pantry mutation. const CART_KEY=KEY+'-cart-receipts'; let cartReceipts={},cartDemo=null; function loadCartReceipts(){try{const data=JSON.parse(localStorage.getItem(CART_KEY)||'{}');cartReceipts={};for(const [week,receipt] of Object.entries(data)){try{const checked=window.CartResults.normalize(receipt);if(checked.week===week)cartReceipts[week]=receipt}catch(e){}}}catch(e){cartReceipts={}}} function cartSnapshot(){const requested=rows().filter(([,row])=>row.need>0).map(([id,row])=>({id,name:meta(id).name,unit:meta(id).unit,qty:buyQty(id,row)}));const destination={name:state.store.name,branch:state.store.branch,method:state.store.method,zip:state.store.zip};return {week:selected,store:[destination.name,destination.branch,destination.method].filter(Boolean).join(' · '),requested,listKey:JSON.stringify({week:selected,destination,requested})}} function currentCartReport(){if(cartDemo?.week===selected)return {report:cartDemo,demo:true};try{const receipt=cartReceipts[selected];if(receipt)return {report:window.CartResults.normalize(receipt),demo:false}}catch(e){}return null} function renderCartResults(){const active=currentCartReport(),notice=$('cart-notification');$('cart-report-slot')?.remove();$('tab-shopping').querySelector('.cart-count')?.remove();notice.hidden=true;notice.innerHTML='';const status=$('cart-status');if(!status)return; if(!active){status.insertAdjacentHTML('afterend','<div id="cart-report-slot" class="cart-preview-row"><button data-action="cart-demo">Preview cart notification</button></div>');return} const {report,demo}=active,stale=!demo&&report.listKey!==cartSnapshot().listKey,s=window.CartResults.summary(report); status.insertAdjacentHTML('afterend','<div id="cart-report-slot">'+window.CartResults.html(report,{demo,stale})+'</div>'); if(!demo&&(s.missing.length||stale)){const text=s.missing.length?s.missing.length+' cart items need attention':'Cart result needs review';$('tab-shopping').insertAdjacentHTML('beforeend','<span class="cart-count" aria-label="'+esc(text)+'">'+(s.missing.length||'!')+'</span>');if(tab!=='shopping'){notice.innerHTML='<button data-action="cart-review">⚠ '+esc(text)+' · Review</button>';notice.hidden=false}} } // A future authorized shopping adapter must supply observed, item-level results. // Recording a receipt does not authorize a shopping run or establish a connection. function recordCartResults(input){const report=window.CartResults.normalize(input);if(!state.weeks[report.week])throw new Error('Unknown meal-plan week.');loadCartReceipts();const old=cartReceipts[report.week];if(old&&Date.parse(old.finishedAt)>Date.parse(input.finishedAt))throw new Error('A newer cart receipt already exists.');cartReceipts[report.week]=clone(input);try{localStorage.setItem(CART_KEY,JSON.stringify(cartReceipts))}catch(e){say('Cart results could not be saved. Keep this page open.');renderCartResults();return false}cartDemo=null;renderCartResults();const s=window.CartResults.summary(report);say(s.missing.length?s.missing.length+' cart items need attention. Review the Shopping List.':'Cart additions confirmed. Review the store cart before checkout.');return true} root.addEventListener('click',e=>{const action=e.target.closest('button')?.dataset.action;if(action==='cart-demo'){const snapshot=cartSnapshot();cartDemo=window.CartResults.demo(selected,snapshot.listKey);renderCartResults();$('cart-report').focus({preventScroll:true});$('cart-report').scrollIntoView({block:'nearest'});say('Example notification only. Your shopping list and pantry are unchanged.')}if(action==='cart-demo-close'){cartDemo=null;renderCartResults();root.querySelector('[data-action="cart-demo"]')?.focus()}if(action==='cart-review'){tab='shopping';closeEditor();render();$('cart-report')?.focus({preventScroll:true});$('cart-report')?.scrollIntoView({block:'nearest'})}if(action==='cart-copy-missing'){const active=currentCartReport();if(active)showCopy(active.demo?'Example missing items':'Cart items needing attention',(active.demo?'DEMO ONLY — not a real cart result\n':'')+window.CartResults.missingText(active.report))}}); window.addEventListener('storage',e=>{if(e.key===CART_KEY){loadCartReceipts();renderCartResults()}}); loadCartReceipts(); render(); window.__planner={state:()=>clone(state),ledger,ingredients,parseIntake,migrate,recipes:R,meta,initialWeek:FIRST,cartSnapshot,recordCartResults}; })(); </script></body></html> ````` END INCLUDED MEAL PLANNER Begin the household questions now, or continue building if my answers are already available. Do not fetch or request a ZIP.

Copy once and paste into your AI chat. The full planner template is included—no download or upload needed.

I’m not Supermom / I just know AI

Meals for your
actual family.

Give your AI one prompt. Build a weekly plan around what your family eats, how much time you have and what’s already in your pantry.

  • 01 Breakfast, lunch and dinner that fit
  • 02 Recipes, cook assignments and a digital pantry
  • 03 One shopping list for your chosen store

AI Meal Planning

Start with your household, ages, preferences, cooking time and store. Your AI asks the useful questions, then builds the planner in the environment you use.

  1. Copy the prompt into your AI chat.
  2. Tell it about your family and what you already have.
  3. Accept, swap or repeat meals and assign who’s cooking.
  4. Review your list and the supported shopping handoff.

Your groceries. Your store.

Choose the actual store you use, including Safeway for direct pickup. With supported tools and an authorized store session, your AI can review products and add approved items to your cart. Otherwise, the planner prepares a copyable shopping request. You review substitutions, missing items and checkout.

Stay in the loop

More things worth trying.

New tools, guides and useful finds for career, finances, home and parenthood.

Optional. You can use every resource without signing up. Privacy