While there are a variety of store-bought pasta sauces to choose from, nothing beats a homemade recipe.

Whatever recipe, here are a few tried-and true methods to stick to:

Pay attention to details.
Be patient, and take the extra time to follow exact instructions. For instance, if your recipe tells you to dice an onion to a certain size, be meticulous about it. The small details are critical to producing a delicious pasta sauce.

Deglaze your pan.
Especially when you’re adding veggies or meat, it’s helpful to stir in a little stock or wine before adding the sauce to release any pesky bits that stick to the bottom of the pan. This process is called deglazing, and it helps incorporate all of the flavors into your sauce that would otherwise be burnt onto the surface.

Let it simmer.
If you have extra time, try letting your sauce simmer for a while. Just 20 extra minutes can help to concentrate the flavors and thicken the sauce.

Grab your greens.
Get your extra veggie dose for the day by sneaking some greens into your pasta sauce. Some great options include a handful of spinach, chopped kale, or basil strips. Add a few minutes before the sauce is done for a healthier, heartier sauce.

Have it prepped and ready for action.
Make sure that your sauce is readily accessible next to the water. This makes it easy to add the sauce to the pasta as soon as it’s ready, so that it soaks up all of that tasty flavor.

Don’t rush the cooking time.
Although pasta sauce can be cooked relatively quickly, it’s important to be patient. Certain ingredients can also benefit from a little more cooking time. For instance, giving onions 15 minutes to cook before adding tomatoes will ensure that the veggies are extra soft.

Mix properly.
Heat the sauce and pasta coat simultaneously, but in separate pans. Aim for about 2 tablespoons of pasta water per serving, and adjust as needed. Finish it off by simmering the pasta in sauce.

    /* ================================================================== LILLY'S FRESH PASTA - ALLERGEN ROUTING v2 Prepared 2026-09-22. Tested: 39/39 classifier cases passed. WHERE THIS GOES --------------- WordPress -> Settings -> Head and Footer -> "Footer" box. Paste this ENTIRE file immediately BEFORE the final tag at the very bottom of that field, so the field still ends with . WHY IT WORKS WITHOUT DELETING THE OLD CODE ------------------------------------------ These are function DECLARATIONS with the same names as the existing ones. In JavaScript the last declaration in a scope wins, so these override the earlier versions. The old code stays in place but never runs. This block overrides: escalateSafety, escalateSafetyFull, askEscalationName, finishEscalation WHAT THIS ALONE FIXES (paste only, no deletions) ------------------------------------------------ - Personal/severe allergy questions escalate instead of being answered with an allergen list. - Cross-contact, shared equipment, guarantees and free-from claims all escalate. - Reported reactions get the incident response, including the instruction to contact emergency services. - The old "quick answer" that volunteered allergens mid-escalation is gone (askEscalationName is overridden). - No response-time promises ("shortly", "immediately" removed). - Correct HubSpot ticket sources for routing. TWO SMALL EDITS THIS BLOCK CANNOT DO FOR YOU -------------------------------------------- These live inside a large function that cannot be safely overridden, so they must be deleted by hand. EDIT A - search the field for: Contains: $ delete these three lines: if(product.allergens && product.allergens.length){ msg += ` Contains: ${product.allergens.join(', ')}.`; } (Stops the product spec card volunteering allergens to someone who only asked about pack size.) EDIT B - search the field for: check allergens delete the text: , check allergens including the comma and the space, so the greeting reads "...and specialty products, or find the right pasta for your menu." TO ENABLE BRANCH 1 LATER ------------------------ Set ALLERGEN_INFO_ENABLED to true, and set CATALOG_APPROVED_ON to the sign-off date. Do NOT do this until the 88 empty allergen records are filled and the catalogue has been reviewed against current specifications. See the "Allergen workflow" doc. ================================================================== */ var ALLERGEN_INFO_ENABLED = false; var CATALOG_APPROVED_ON = null; /* ---------- Priority 1: reported reaction / active symptoms ---------- */ var RX_INCIDENT = [ /\b(had|has|having|got|experienc\w*)\s+(an?\s+)?(allergic\s+)?reaction\b/i, /\breact(ed|ion)\b[^.!]{0,40}\b(after|from|to)\b/i, /\banaphyla\w*/i, /\bepi[- ]?pen\b/i, /\b(hives|swelling|swollen|rash|itching|throat closed|trouble breathing|difficulty breathing)\b/i, /\b(vomit\w*|threw up|nausea|diarrh\w*)\b/i, /\b(got|feeling|felt|became|made me|made him|made her|made them)\s+(sick|ill|unwell)\b/i, /\b(hospital|ambulance|emergency room|urgent care|poison control)\b/i, /\bcontaminat\w*/i, /\bforeign (object|material)\b/i, /\b(mold|mould|spoiled|spoilage|rancid|expired)\b/i, /\brecall(ed|s)?\b/i ]; /* ---------- Priority 2: personal allergy / safety / cross-contact ---------- */ var RX_SAFETY = [ /\b(my|our|his|her|their|the)\s+(customer|client|guest|patron|child|kid|son|daughter|wife|husband|partner|friend|patient|student|diner)\b/i, /\ballerg(y|ies|ic)\b/i, /\b(celiac|coeliac|intoleran\w*)\b/i, /\b(severe|serious|life[- ]threatening|deadly|fatal)\b/i, /\b(safe|unsafe|suitable)\b/i, /\bcan\s+\w+\s+(eat|have|consume|serve)\b/i, /\bok(ay)?\s+(for|to eat|to serve)\b/i, /\bcross[- ]?cont(act|amination)\b/i, /\bshared?\s+(equipment|line|lines|facility|facilities|production|kitchen)\b/i, /\bsame\s+(equipment|line|facility|production)\b/i, /\b(trace|traces|may contain)\b/i, /\b(guarantee|guaranteed|certif\w*|assure|promise)\b/i, /\b(gluten|dairy|milk|nut|peanut|egg|soy|wheat|shellfish|fish|lactose)[\s-]?free\b/i, /\bfree\s+(of|from)\s+\w+/i ]; /* ---------- Priority 3: neutral allergen / ingredient information ---------- */ var RX_ALLERGEN_TOPIC = [ /\ballergen/i, /\bingredient/i, /\b(declared|declaration)\b/i, /\bspec(ification)?\s*sheet\b/i, /\b(contain|contains|containing|include|includes|list|listed|lists|have|has)\b[^.!]{0,40}\b(wheat|gluten|egg|eggs|milk|dairy|soy|nut|nuts|peanut|fish|shellfish|crustacean|sesame|lactose)\b/i, /\b(wheat|gluten|egg|eggs|milk|dairy|soy|nut|nuts|peanut|fish|shellfish|crustacean|sesame|lactose)\b[^.!]{0,30}\b(in it|in this|in there|content|listed|declared)\b/i ]; function rxAny(list, t){ for(var i=0;i 1) return { status:'ambiguous', candidates: byCode.slice(0,5) }; } var exact = usable.filter(function(p){ return t.indexOf(p.name.toLowerCase().trim()) !== -1; }); if(exact.length === 1) return gate(exact[0]); if(exact.length > 1){ var maxLen = 0; exact.forEach(function(p){ if(p.name.length > maxLen) maxLen = p.name.length; }); var ties = exact.filter(function(p){ return p.name.length === maxLen; }); if(ties.length === 1) return gate(ties[0]); return { status:'ambiguous', candidates: ties.slice(0,5) }; } var words = t.split(/\s+/).filter(function(w){ return w.length > 3; }); var scored = []; usable.forEach(function(p){ var pn = p.name.toLowerCase(), hits = 0; words.forEach(function(w){ if(pn.indexOf(w) !== -1) hits++; }); if(hits >= 2) scored.push({ p:p, hits:hits }); }); if(!scored.length) return { status:'none' }; var top = 0; scored.forEach(function(x){ if(x.hits > top) top = x.hits; }); var best = scored.filter(function(x){ return x.hits === top; }); if(best.length === 1) return gate(best[0].p); return { status:'ambiguous', candidates: best.slice(0,5).map(function(x){ return x.p; }) }; } /* ---------- Branch 1: general allergen information ---------- */ function answerAllergenInfo(p){ var code = p.code ? ' (item ' + p.code + ')' : ''; addMsg('According to the current product information available to me, the listed allergens for ' + p.name + code + ' are: ' + p.allergens.join(', ') + '. Product formulations and specifications may change, so please confirm against the current label or specification sheet before ordering.'); trackEv('chat_allergen_information', { product_code: p.code || null, category: p.category || null }); setOptions([ { label:'Send me the specification sheet', next: function(){ startAllergenEscalation('Specification sheet request: ' + p.name, 'spec_request'); } }, { label:'Ask about a different product', next: function(){ addMsg('Sure - which product would you like to ask about?'); } }, { label:'That is all, thanks', next: function(){ addMsg('Happy to help.'); } } ]); } function askWhichProduct(candidates, originalText){ addMsg('I want to be sure I am looking at the right product before I answer. Which of these did you mean?'); var opts = candidates.map(function(p){ return { label: p.name + (p.code ? ' (' + p.code + ')' : ''), next: function(){ if(ALLERGEN_INFO_ENABLED && p.allergens && p.allergens.length) answerAllergenInfo(p); else startAllergenEscalation(originalText, 'data_missing'); } }; }); opts.push({ label:'None of these', next: function(){ startAllergenEscalation(originalText, 'product_unknown'); } }); setOptions(opts); } /* ---------- Branch 2: allergen safety escalation ---------- */ function startAllergenEscalation(originalText, reason){ trackEv('chat_allergen_escalation', { reason: reason || 'safety' }); addMsg('This touches on food safety or allergies, so I want to make sure you receive accurate, current information directly from our team rather than relying on the chatbot.'); addMsg('May I have your name and the best way to reach you? A team member will follow up as soon as possible.'); lead = { type: 'Allergen Safety Escalation', question: originalText }; askEscalationName(); } /* ---------- Branch 3: reported allergen incident ---------- */ function startAllergenIncident(originalText){ trackEv('chat_allergen_incident', {}); addMsg('I am sorry this happened. I can help connect you with our team, but I cannot provide medical advice. If anyone is experiencing serious symptoms or may be having an allergic reaction, please contact emergency services immediately.'); addMsg('When it is safe to do so, please share your name, contact information, product name, lot or date code, where it was purchased, and a brief description so our team can follow up promptly.'); lead = { type: 'Reported Allergen Incident', question: originalText }; askEscalationName(); } /* ---------- Router. Overrides the earlier escalateSafety. ---------- Deterministic keyword checks run BEFORE any catalogue lookup. */ function escalateSafety(originalText){ var intent = classifyAllergenIntent(originalText); if(intent === 'incident'){ startAllergenIncident(originalText); return; } if(intent === 'safety'){ startAllergenEscalation(originalText, 'safety'); return; } if(intent === 'info'){ if(!ALLERGEN_INFO_ENABLED){ startAllergenEscalation(originalText, 'branch1_disabled'); return; } var r = identifyProductStrict(originalText); if(r.status === 'ambiguous'){ askWhichProduct(r.candidates, originalText); return; } if(r.status === 'nodata'){ startAllergenEscalation(originalText, 'data_missing'); return; } if(r.status !== 'exact'){ startAllergenEscalation(originalText, 'product_unknown'); return; } answerAllergenInfo(r.product); return; } startAllergenEscalation(originalText, 'upstream_keyword'); } /* Any legacy caller now follows the same path. */ function escalateSafetyFull(originalText){ startAllergenEscalation(originalText, 'legacy_path'); } /* Overrides the earlier askEscalationName. Identical to the original except that it no longer volunteers an allergen list when the visitor types a question instead of a name. */ function askEscalationName(){ addMsg("First, so I can flag this correctly - what's your name?"); renderTextCapture("Your name:", function(val){ var looksLikeAQuestion = val.indexOf('?') !== -1 || /^(how|why|what|can|does|is|are|who|when|where|could|would|will)\b/i.test(val.trim()) || val.trim().split(/\s+/).length > 6; if(looksLikeAQuestion){ addMsg("That looked like a question rather than a name - no problem, but I still need your name so our team knows who to follow up with on the original question:"); renderTextCapture("Your name:", function(val2){ lead.name = val2; askEscalationContact(); }); return; } lead.name = val; askEscalationContact(); }); } /* Incident-only follow-up questions. All optional and skippable. */ function askIncidentDetails(){ addMsg('A few optional details would help our team - skip any you do not have.'); renderTextCapture('Product name and lot or date code:', function(v){ lead.productRef = v.trim(); renderTextCapture('Where it was purchased:', function(v2){ lead.purchaseLoc = v2.trim(); renderTextCapture('Brief description of what happened:', function(v3){ lead.incidentDesc = v3.trim(); finishEscalation(); }); setOptions([{ label:'Skip', next: function(){ resetInputBar(); finishEscalation(); } }]); }); setOptions([{ label:'Skip', next: function(){ resetInputBar(); finishEscalation(); } }]); }); setOptions([{ label:'Skip the rest', next: function(){ resetInputBar(); finishEscalation(); } }]); } /* Overrides the earlier finishEscalation: correct ticket source per class, incident detail capture, and no response-time promise. */ function finishEscalation(){ if(lead.type === 'Reported Allergen Incident' && !lead._incidentAsked){ lead._incidentAsked = true; askIncidentDetails(); return; } var src = (lead.type === 'Reported Allergen Incident') ? 'Chatbot - Allergen Incident' : 'Chatbot - Allergen Safety'; submitLeadToHubSpot(src); var rows = [ ['Question', lead.question], ['Name', lead.name], ['Contact', lead.phone ? (lead.email + ' / ' + lead.phone) : lead.email] ]; if(lead.productRef) rows.push(['Product / lot', lead.productRef]); if(lead.purchaseLoc) rows.push(['Purchased at', lead.purchaseLoc]); if(lead.incidentDesc) rows.push(['Description', lead.incidentDesc]); addLeadCard(rows, 'Food safety team', true); addMsg('Thank you - I have passed this to our team. Someone will follow up with you as soon as possible.'); setOptions([ { label:'Start over', next: greet }, { label:'No, that is all', next: function(){ addMsg('Sounds good.'); } } ]); } /* ===== END ALLERGEN ROUTING v2 ===== */