<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:dcterms="http://purl.org/dc/terms/"
     xmlns:media="http://search.yahoo.com/mrss/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:cf="https://www.futureplc.com/rss/content-flags"
>
    <channel>
                    <atom:link href="https://www.tomsguide.com/feeds/tag/kitchen-dining" rel="self" type="application/rss+xml" />
                            <title><![CDATA[ Latest from Tom's Guide in Kitchen-dining ]]></title>
                <link>https://www.tomsguide.com/home/home-appliances/kitchen-dining</link>
        <description><![CDATA[ All the latest kitchen-dining content from the Tom's Guide team ]]></description>
                                    <lastBuildDate>Sat, 12 Sep 2026 13:00:00 +0000</lastBuildDate>
                            <language>en</language>
                                <item>
                                                            <title><![CDATA[ Boldly go into the kitchen: Le Creuset just dropped an incredible Star Trek cookware collection ]]></title>
                                                                                                <dc:content><![CDATA[ <p>It was the 60th anniversary of Star Trek earlier this week. I published a round-up of some of the <a href="https://www.tomsguide.com/entertainment/movies/boldly-celebrate-star-treks-60th-anniversary-with-these-top-blu-rays-and-merch-items">best merchandise for fans</a>, but missed arguably the best of the bunch: the brand new range of Trek-themed Le Creuset products!</p><p>There's some incredible items here, all themed around the original series and its iconic characters and symbols. How about a Spock mug with an ear? Or a Mini Cocotte based on the Gorn or creepy Balok? Or a "Live long and prosper" oven mitt?</p><p>Le Creuset is known for making some of the most durable cookware out there, so why not liven up your kitchen with some of these fun and no doubt highly collectible Star Trek pieces?</p>        <div class="featured_product_block featured_block_hero" data-id="5247b32a-adf1-11f1-8c6e-9dc83562b59c">            <a href="https://www.lecreuset.com/star-trek-collection-spock-mug-pearlized-creme-14-fl-oz/72065140759004.html" data-model-name="Spock Mug" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/FDq6c53ZZGm387GfgQN7gF.jpg" alt="Le Creuset Spock Mug"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Spock Mug</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b3a2-adf1-11f1-bd6c-5537178eed7e">            <a href="https://www.lecreuset.com/star-trek-collection-live-long-and-prosper%E2%84%A2-oven-mitt-unassigned-15-in/45102038046001.html" data-model-name="Live Long & Prosper Oven Mitt" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/5rcPxn3mDyJSmiRUPWbsK7.jpg" alt="Live Long & Prosper Oven Mitt"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Live Long & Prosper Oven Mitt</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b410-adf1-11f1-8017-b94be36200c2">            <a href="https://www.lecreuset.com/star-trek-collection-live-long-and-prosper%E2%84%A2-spoon-rest-bleu-riviera-7-5-in/72066019099004.html" data-model-name="Live Long & Prosper Spoon Rest" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/GyGdVfr879kSod7wgfCRFe.jpg" alt="Live Long & Prosper Spoon Rest"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Live Long & Prosper Spoon Rest</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b5d2-adf1-11f1-bee9-cfb4f1b4e7e1">            <a href="https://www.lecreuset.com/star-trek-collection-balok-mini-cocotte-pearlized-bleu-riviera-10-fl-oz/72062130075004.html" data-model-name="Balok Mini Cocotte" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/NMFqqZLsfJX9HmVttpY4vh.jpg" alt="Star Trek Collection Balok Mini Cocotte"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Balok Mini Cocotte</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b64a-adf1-11f1-80c0-6b2d9ef343fc">            <a href="https://www.lecreuset.com/star-trek-collection-gorn-mini-cocotte-pearlized-bamboo-green-10-fl-oz/72064130077004.html" data-model-name="Gorn Mini Cocotte" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/mFkPQDofFRjBybTZB3389.jpg" alt="Star Trek Collection Gorn Mini Cocotte"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Gorn Mini Cocotte</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b6b8-adf1-11f1-8b37-8b9af49d4948">            <a href="https://www.lecreuset.com/star-trek-collection-communi-grater-6-in/41079015001001.html" data-model-name="Communi-Grater" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/kqMd9tvXXFZ2adViMgbT6a.jpg" alt="Le Creuset, Star Trek Collection Communi-Grater"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Communi-Grater</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b71c-adf1-11f1-9f4c-2930fbc55316">            <a href="https://www.lecreuset.com/star-trek-collection-galileo-shuttlecraft-rectangular-covered-casserole-brioche-1-7-qt/72067260064004.html" data-model-name="Galileo Shuttlecraft Covered Casserole" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/dipdVZBvFagqr37jXgN3MP.jpg" alt="Le Creuset, Star Trek Collection Galileo Shuttlecraft Rectangular Covered Casserole"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Galileo Shuttlecraft Covered Casserole</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b78a-adf1-11f1-8c18-eb78cdea9cd9">            <a href="https://www.lecreuset.com/star-trek-collection-klingon-round-braiser-with-klingon-trefoil-knob-nuit-3-5-qt/21953030129101.html" data-model-name="Klingon Round Braiser with Klingon Knob" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/PBj2LfgLQeWpjpmmv8rVvV.jpg" alt="Le Creuset, Star Trek Collection Klingon Round Braiser with Klingon Trefoil Knob"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Klingon Round Braiser with Klingon Knob</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b7ee-adf1-11f1-bca5-e9d11258932c">            <a href="https://www.lecreuset.com/star-trek-collection-enterprise-round-dutch-oven-with-starfleet-delta-knob-french-grey-5-5-qt/21952026541291.html" data-model-name="Enterprise Round Dutch Oven" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/DrCWj7QVW5vWiDosJzCruS.jpg" alt="Le Creuset, Star Trek Collection Enterprise Round Dutch Oven with Starfleet Delta Knob"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Enterprise Round Dutch Oven</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/boldly-go-into-the-kitchen-le-creuset-just-dropped-an-incredible-star-trek-cookware-collection</link>
                                                                            <description>
                            <![CDATA[ Set your kitchen to stun with Le Creuset's amazing new Star Trek cookware line: iconic and durable pieces so that Trek fans can cook like a captain. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">hR58wYsGqhn5rHF7raMbiY</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/jFiXnEg8voJGBiDoaiQzL5-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 12 Sep 2026 13:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                                    <dc:creator><![CDATA[ Will Salmon ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/HpRWP7Mh9ErgRGoTvZBbBJ.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Will Salmon is Tom&#039;s Guide&#039;s Collections Editor, curating advice on the best products to spend your money on. Will has been writing about tech and entertainment for almost 20 years, including stints as GamesRadar+&#039;s Streaming Editor, editing comics website Newsarama, and writing for lots and lots of magazines.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/jFiXnEg8voJGBiDoaiQzL5-1280-80.jpg">
                                                            <media:credit><![CDATA[Le Creuset / Edited with Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Star Trek Le Creuset collection]]></media:description>                                                            <media:text><![CDATA[Star Trek Le Creuset collection]]></media:text>
                                <media:title type="plain"><![CDATA[Star Trek Le Creuset collection]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/jFiXnEg8voJGBiDoaiQzL5-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It was the 60th anniversary of Star Trek earlier this week. I published a round-up of some of the <a href="https://www.tomsguide.com/entertainment/movies/boldly-celebrate-star-treks-60th-anniversary-with-these-top-blu-rays-and-merch-items">best merchandise for fans</a>, but missed arguably the best of the bunch: the brand new range of Trek-themed Le Creuset products!</p><p>There's some incredible items here, all themed around the original series and its iconic characters and symbols. How about a Spock mug with an ear? Or a Mini Cocotte based on the Gorn or creepy Balok? Or a "Live long and prosper" oven mitt?</p><p>Le Creuset is known for making some of the most durable cookware out there, so why not liven up your kitchen with some of these fun and no doubt highly collectible Star Trek pieces?</p>        <div class="featured_product_block featured_block_hero" data-id="5247b32a-adf1-11f1-8c6e-9dc83562b59c">            <a href="https://www.lecreuset.com/star-trek-collection-spock-mug-pearlized-creme-14-fl-oz/72065140759004.html" data-model-name="Spock Mug" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/FDq6c53ZZGm387GfgQN7gF.jpg" alt="Le Creuset Spock Mug"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Spock Mug</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b3a2-adf1-11f1-bd6c-5537178eed7e">            <a href="https://www.lecreuset.com/star-trek-collection-live-long-and-prosper%E2%84%A2-oven-mitt-unassigned-15-in/45102038046001.html" data-model-name="Live Long & Prosper Oven Mitt" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/5rcPxn3mDyJSmiRUPWbsK7.jpg" alt="Live Long & Prosper Oven Mitt"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Live Long & Prosper Oven Mitt</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b410-adf1-11f1-8017-b94be36200c2">            <a href="https://www.lecreuset.com/star-trek-collection-live-long-and-prosper%E2%84%A2-spoon-rest-bleu-riviera-7-5-in/72066019099004.html" data-model-name="Live Long & Prosper Spoon Rest" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/GyGdVfr879kSod7wgfCRFe.jpg" alt="Live Long & Prosper Spoon Rest"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Live Long & Prosper Spoon Rest</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b5d2-adf1-11f1-bee9-cfb4f1b4e7e1">            <a href="https://www.lecreuset.com/star-trek-collection-balok-mini-cocotte-pearlized-bleu-riviera-10-fl-oz/72062130075004.html" data-model-name="Balok Mini Cocotte" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/NMFqqZLsfJX9HmVttpY4vh.jpg" alt="Star Trek Collection Balok Mini Cocotte"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Balok Mini Cocotte</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b64a-adf1-11f1-80c0-6b2d9ef343fc">            <a href="https://www.lecreuset.com/star-trek-collection-gorn-mini-cocotte-pearlized-bamboo-green-10-fl-oz/72064130077004.html" data-model-name="Gorn Mini Cocotte" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/mFkPQDofFRjBybTZB3389.jpg" alt="Star Trek Collection Gorn Mini Cocotte"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Gorn Mini Cocotte</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b6b8-adf1-11f1-8b37-8b9af49d4948">            <a href="https://www.lecreuset.com/star-trek-collection-communi-grater-6-in/41079015001001.html" data-model-name="Communi-Grater" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/kqMd9tvXXFZ2adViMgbT6a.jpg" alt="Le Creuset, Star Trek Collection Communi-Grater"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Communi-Grater</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b71c-adf1-11f1-9f4c-2930fbc55316">            <a href="https://www.lecreuset.com/star-trek-collection-galileo-shuttlecraft-rectangular-covered-casserole-brioche-1-7-qt/72067260064004.html" data-model-name="Galileo Shuttlecraft Covered Casserole" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/dipdVZBvFagqr37jXgN3MP.jpg" alt="Le Creuset, Star Trek Collection Galileo Shuttlecraft Rectangular Covered Casserole"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Galileo Shuttlecraft Covered Casserole</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b78a-adf1-11f1-8c18-eb78cdea9cd9">            <a href="https://www.lecreuset.com/star-trek-collection-klingon-round-braiser-with-klingon-trefoil-knob-nuit-3-5-qt/21953030129101.html" data-model-name="Klingon Round Braiser with Klingon Knob" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/PBj2LfgLQeWpjpmmv8rVvV.jpg" alt="Le Creuset, Star Trek Collection Klingon Round Braiser with Klingon Trefoil Knob"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Klingon Round Braiser with Klingon Knob</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5247b7ee-adf1-11f1-bca5-e9d11258932c">            <a href="https://www.lecreuset.com/star-trek-collection-enterprise-round-dutch-oven-with-starfleet-delta-knob-french-grey-5-5-qt/21952026541291.html" data-model-name="Enterprise Round Dutch Oven" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/DrCWj7QVW5vWiDosJzCruS.jpg" alt="Le Creuset, Star Trek Collection Enterprise Round Dutch Oven with Starfleet Delta Knob"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Le Creuset</div>                                        <div class="featured__title">Enterprise Round Dutch Oven</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I got a first look at Ooni’s Halo Core spiral mixer: Three things I love and one thing I don’t ]]></title>
                                                                                                <dc:content><![CDATA[ <p>When Ooni announced the new<a href="https://www.tomsguide.com/home/kitchen-dining/watch-out-kitchenaid-oonis-launching-its-second-stand-mixer-and-at-usd599-it-price-matches-the-artisan-pro"> Halo Core spiral mixer</a>, one thought immediately crossed my mind: KitchenAid is in for some serious competition. </p><p>Available for <a href="https://www.amazon.com/Ooni-Spiral-Dough-Mixer-Stand/dp/B0H5RCW4W2/ref=sr_1_1?">$599 at Amazon,</a> it hits the exact price as the iconic<a href="https://www.tomsguide.com/home/kitchenaid-artisan-plus-mixer-review"> KitchenAid Artisan Plus</a>, but offers a more compact and home-kitchen-friendly footprint than the high-end<a href="https://www.tomsguide.com/home/home-appliances/ooni-halo-pro-spiral-stand-mixer-review"> Ooni Halo Pro spiral mixer</a> — currently our top pick for bread making in our <a href="https://www.tomsguide.com/best-picks/best-stand-mixers">best standing mixer buying guide</a>.</p><p>While $599 is still a real investment, the Halo Core feels far more accessible than the Ooni Halo Pro, which is <a href="https://www.amazon.com/dp/B0F746CZ3L?">$799 at Amazon, </a>plus it won't take up as much countertop space in your kitchen.</p><p>I’ve been putting the Halo Core through its paces, and there’s a lot to like. I’ll follow up with a full review in a couple of weeks, but for now, here are three things I loved and one thing you should know.</p><div class="product"><a data-dimension112="a5697ee6-ac5f-11f1-9d67-15deb1193abc" data-action="Deal Block" data-label="Halo Core Spiral Mixer" data-dimension48="Halo Core Spiral Mixer" data-dimension25="$599" href="https://www.amazon.com/Ooni-Spiral-Dough-Mixer-Stand/dp/B0H5RCW4W2?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:424px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="wcvYr66Fer4q8dWMPz26jb" name="Ooni Halo Core stand mixer in Black" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/wcvYr66Fer4q8dWMPz26jb.png" mos="" align="middle" fullscreen="" width="424" height="424" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ooni Halo Core spiral mixer uses the same dual-kneading action that pro bakers trust. The spiral dough hook and breaker bar work with the rotating bowl to build a strong gluten structure, resulting in a light, soft dough. The mixer also comes complete with a 4-8 qt bowl, a dual-geared whisk and an angled silicone edge beater for making cake batters, cookies and whisking cream and meringues. <a class="view-deal button" href="https://www.amazon.com/Ooni-Spiral-Dough-Mixer-Stand/dp/B0H5RCW4W2?" target="_blank" rel="nofollow" data-dimension112="a5697ee6-ac5f-11f1-9d67-15deb1193abc" data-action="Deal Block" data-label="Halo Core Spiral Mixer" data-dimension48="Halo Core Spiral Mixer" data-dimension25="$599">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Oajm5X"></div>                            </div>                            <script src="https://kwizly.com/embed/Oajm5X.js" async></script><h3 class="article-body__section" id="section-ooni-halo-core-spiral-mixer-what-i-like"><span>Ooni Halo Core spiral mixer: What I like</span></h3><h2 id="1-perfect-dough">1. Perfect dough</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="V3UWaYeKvCNTVk2UHEhaKe" name="Ooni Halo Core dough hook and breaker bar" alt="Ooni Halo Core dough hook and breaker bar" src="https://cdn.mos.cms.futurecdn.net/V3UWaYeKvCNTVk2UHEhaKe.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>The Halo Core spiral mixer, like its older sibling, is designed specifically for bread making. Instead of the planetary stand mixers home bakers are used to, Ooni’s design brings professional-grade spiral mixing into our kitchens.</p><p>How does it work? The dough hook and bowl rotate around a breaker bar — a stationary vertical metal rod that sits in the mixing bowl. Although watching the rotating bowl is fun to watch, it’s not a gimmick.  </p><p>The breaker bar removes dough from the hook as it spins during mixing, preventing it from climbing up the dough hook and stopping you from pausing the mix to remove the dough. It also stretches the dough for a stronger gluten structure.</p><p>I’ve been experimenting with making bread and pizza dough, and although the pizza dough is currently proving in my kitchen, the bread tasted exceptional. In fact, my husband didn’t realize it was homemade. </p><p>I also found that the kneading process was smooth. The usual mixers I test move around my countertop when the dough hook is in action, and it almost seems like too much for the appliance’s motor. There was a clear difference when using the Ooni Halo Core. The mixer stayed steady, and there was no hint of any electric smell or the motor burning out. </p><h2 id="2-pause-and-go-again">2. Pause and go again</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xvxCmZ72M4HgX76QvbkzgJ" name="Ooni Halo Core control" alt="Ooni Halo Core stand mixer control" src="https://cdn.mos.cms.futurecdn.net/xvxCmZ72M4HgX76QvbkzgJ.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>The controls of the Halo Core work differently from any stand mixer I’ve previously tested, but in a good way. The control knob is positioned on top of the appliance, so it’s easy to access whether you’re right- or left-handed. What’s more, it works in percentages, with 20 speeds. Turn it clockwise once for 5% at 60 rpm, and turn it up to 100% for 300 rpm. </p><p>The control lights up in sections, so you can clearly see which speed setting you're on. This is quite clever in itself, but what impressed me more was that you can simply press the knob and the appliance will pause. Press it again, and it will start from where it left off. It may seem like a small thing, but I found it took away the hassle of turning down the speed when you want to pause the mix.</p><h2 id="3-double-whisk">3. Double whisk </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="meZBSQFjrR6TbefkE39twD" name="Ooni Halo Core stand mixer" alt="Ooni Halo Core stand mixer" src="https://cdn.mos.cms.futurecdn.net/meZBSQFjrR6TbefkE39twD.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I’ve never made butter before, but since my colleague made butter with the Halo Pro, I followed suit. I started by attaching the dual-geared whisk attachment — it even has a built-in gearbox for rapid whipping! </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3WnNedwWrnXHs2HuURMsVU" name="Ooni Halo Core stand mixer buttermilk separating" alt="Ooni Halo Core stand mixer buttermilk separating" src="https://cdn.mos.cms.futurecdn.net/3WnNedwWrnXHs2HuURMsVU.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>The whisk really came into its own, whipping the heavy cream into what looked like scrambled eggs until the fat and buttermilk separated. Apart from the speed, it’s fascinating to watch, and if you’ve never made butter before, I recommend giving it a go. </p><p>The whisk attachment proved that the Halo Core isn’t just good at making dough; it has a firm place in the kitchen for other baking tasks. </p><h3 class="article-body__section" id="section-ooni-halo-core-spiral-mixer-what-i-don-t-like"><span>Ooni Halo Core spiral mixer: What I don't like</span></h3><h2 id="it-wasn-39-t-great-at-mixing-cake-batter">It wasn't great at mixing cake batter</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ubR55uboGghEWXDKiHxLhD" name="Ooni Halo Core stand mixer" alt="Ooni Halo Core stand mixer" src="https://cdn.mos.cms.futurecdn.net/ubR55uboGghEWXDKiHxLhD.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>After the whisk's success, I was excited to try the angled silicone-edge beater to make an all-in-one cake batter. I love the silicone beater I’ve used with the KitchenAid Artisan Plus, as it swipes around the edge of the bowl, incorporating the whole mixture — so you don’t need to stop midway through to scrape the ingredients off the side.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kToJx2jJvD2CzLkw74AwjJ" name="Ooni Halo Core stand mixer cake batter" alt="Ooni Halo Core stand mixer making cake batter" src="https://cdn.mos.cms.futurecdn.net/kToJx2jJvD2CzLkw74AwjJ.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>The Halo Core did the same, but it seemed to struggle to incorporate the butter into the mixture, even though it was soft when I added it to the bowl. Then, when I raised the mixer’s head, I noticed a large clump of butter was still attached to the beater. </p><p>I’m going to keep testing its cake-making ability, since other reviews have given it more positive results. So keep an eye out for my full review.</p><p><strong>Over to you</strong></p><p>Are you thinking about buying an Ooni mixer, or have you already invested in one? I’d love to read your thoughts and experiences in the comments below.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchenaid-vs-cuisinart-which-stand-mixer-is-best">KitchenAid vs Cuisinart: Which stand mixer is best?</a></li><li><a href="https://www.tomsguide.com/home/smegs-newest-stand-mixer-gives-kitchenaid-a-run-for-its-money-heres-why-it-won-me-over">Smeg’s newest retro stand mixer gives KitchenAid a run for its money — here’s why it won me over</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/i-tested-oonis-new-rotating-stone-and-its-your-ticket-to-perfect-pizza-at-home">I tested Ooni's new Rotating Stone — and it's your ticket to perfect pizza at home</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/home-appliances/i-got-a-first-look-at-oonis-halo-core-spiral-mixer-three-things-i-love-and-one-thing-i-dont</link>
                                                                            <description>
                            <![CDATA[ Ooni’s Halo Core Spiral Mixer is impressive, but it’s not quite perfect. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">yach34Xxmy7hnYRdgrGLyD</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/eqfbZQsffwyta5HQWBUmX7-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Thu, 10 Sep 2026 08:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                                                                <author><![CDATA[ camilla.sharman@futurenet.com (Camilla Sharman) ]]></author>                    <dc:creator><![CDATA[ Camilla Sharman ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/nY4nvWzofHKHpvzAqN5LVH.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Camilla is the Homes Staff Writer and covers everything to do with homes and gardens. She has a wealth of editorial experience, mounting over 30 years, and covers news and features, tests products for reviews and compiles buying guides.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Her work has appeared in business and consumer titles, including Ideal Home, Real Homes, House Beautiful, Homebuilding &amp;amp; Renovation, and Kitchen &amp;amp; Bathroom Business. She’s even appeared on the cover of Your Home, writing about her own house renovation.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Although she’s obsessed with decorating her home, she also enjoys baking and trying out the latest kitchen appliances. But when she’s not inside, you’ll find her pottering about in her yard, tending to her vegetable patch or taking in her prized hydrangeas. She also enjoys keeping fit, and if she&#039;s not on a spin bike trying to keep up with the class, she&#039;ll be in the pool, or trying to perfect her headstand in a yoga class.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/png" url="https://cdn.mos.cms.futurecdn.net/eqfbZQsffwyta5HQWBUmX7-1280-80.png">
                                                            <media:credit><![CDATA[Future / Camilla Sharman]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Camilla Sharman with Ooni Halo Core spiral mixer]]></media:description>                                                            <media:text><![CDATA[Camilla Sharman with Ooni Halo Core spiral mixer]]></media:text>
                                <media:title type="plain"><![CDATA[Camilla Sharman with Ooni Halo Core spiral mixer]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/eqfbZQsffwyta5HQWBUmX7-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>When Ooni announced the new<a href="https://www.tomsguide.com/home/kitchen-dining/watch-out-kitchenaid-oonis-launching-its-second-stand-mixer-and-at-usd599-it-price-matches-the-artisan-pro"> Halo Core spiral mixer</a>, one thought immediately crossed my mind: KitchenAid is in for some serious competition. </p><p>Available for <a href="https://www.amazon.com/Ooni-Spiral-Dough-Mixer-Stand/dp/B0H5RCW4W2/ref=sr_1_1?">$599 at Amazon,</a> it hits the exact price as the iconic<a href="https://www.tomsguide.com/home/kitchenaid-artisan-plus-mixer-review"> KitchenAid Artisan Plus</a>, but offers a more compact and home-kitchen-friendly footprint than the high-end<a href="https://www.tomsguide.com/home/home-appliances/ooni-halo-pro-spiral-stand-mixer-review"> Ooni Halo Pro spiral mixer</a> — currently our top pick for bread making in our <a href="https://www.tomsguide.com/best-picks/best-stand-mixers">best standing mixer buying guide</a>.</p><p>While $599 is still a real investment, the Halo Core feels far more accessible than the Ooni Halo Pro, which is <a href="https://www.amazon.com/dp/B0F746CZ3L?">$799 at Amazon, </a>plus it won't take up as much countertop space in your kitchen.</p><p>I’ve been putting the Halo Core through its paces, and there’s a lot to like. I’ll follow up with a full review in a couple of weeks, but for now, here are three things I loved and one thing you should know.</p><div class="product"><a data-dimension112="a5697ee6-ac5f-11f1-9d67-15deb1193abc" data-action="Deal Block" data-label="Halo Core Spiral Mixer" data-dimension48="Halo Core Spiral Mixer" data-dimension25="$599" href="https://www.amazon.com/Ooni-Spiral-Dough-Mixer-Stand/dp/B0H5RCW4W2?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:424px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="wcvYr66Fer4q8dWMPz26jb" name="Ooni Halo Core stand mixer in Black" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/wcvYr66Fer4q8dWMPz26jb.png" mos="" align="middle" fullscreen="" width="424" height="424" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ooni Halo Core spiral mixer uses the same dual-kneading action that pro bakers trust. The spiral dough hook and breaker bar work with the rotating bowl to build a strong gluten structure, resulting in a light, soft dough. The mixer also comes complete with a 4-8 qt bowl, a dual-geared whisk and an angled silicone edge beater for making cake batters, cookies and whisking cream and meringues. <a class="view-deal button" href="https://www.amazon.com/Ooni-Spiral-Dough-Mixer-Stand/dp/B0H5RCW4W2?" target="_blank" rel="nofollow" data-dimension112="a5697ee6-ac5f-11f1-9d67-15deb1193abc" data-action="Deal Block" data-label="Halo Core Spiral Mixer" data-dimension48="Halo Core Spiral Mixer" data-dimension25="$599">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Oajm5X"></div>                            </div>                            <script src="https://kwizly.com/embed/Oajm5X.js" async></script><h3 class="article-body__section" id="section-ooni-halo-core-spiral-mixer-what-i-like"><span>Ooni Halo Core spiral mixer: What I like</span></h3><h2 id="1-perfect-dough">1. Perfect dough</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="V3UWaYeKvCNTVk2UHEhaKe" name="Ooni Halo Core dough hook and breaker bar" alt="Ooni Halo Core dough hook and breaker bar" src="https://cdn.mos.cms.futurecdn.net/V3UWaYeKvCNTVk2UHEhaKe.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>The Halo Core spiral mixer, like its older sibling, is designed specifically for bread making. Instead of the planetary stand mixers home bakers are used to, Ooni’s design brings professional-grade spiral mixing into our kitchens.</p><p>How does it work? The dough hook and bowl rotate around a breaker bar — a stationary vertical metal rod that sits in the mixing bowl. Although watching the rotating bowl is fun to watch, it’s not a gimmick.  </p><p>The breaker bar removes dough from the hook as it spins during mixing, preventing it from climbing up the dough hook and stopping you from pausing the mix to remove the dough. It also stretches the dough for a stronger gluten structure.</p><p>I’ve been experimenting with making bread and pizza dough, and although the pizza dough is currently proving in my kitchen, the bread tasted exceptional. In fact, my husband didn’t realize it was homemade. </p><p>I also found that the kneading process was smooth. The usual mixers I test move around my countertop when the dough hook is in action, and it almost seems like too much for the appliance’s motor. There was a clear difference when using the Ooni Halo Core. The mixer stayed steady, and there was no hint of any electric smell or the motor burning out. </p><h2 id="2-pause-and-go-again">2. Pause and go again</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xvxCmZ72M4HgX76QvbkzgJ" name="Ooni Halo Core control" alt="Ooni Halo Core stand mixer control" src="https://cdn.mos.cms.futurecdn.net/xvxCmZ72M4HgX76QvbkzgJ.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>The controls of the Halo Core work differently from any stand mixer I’ve previously tested, but in a good way. The control knob is positioned on top of the appliance, so it’s easy to access whether you’re right- or left-handed. What’s more, it works in percentages, with 20 speeds. Turn it clockwise once for 5% at 60 rpm, and turn it up to 100% for 300 rpm. </p><p>The control lights up in sections, so you can clearly see which speed setting you're on. This is quite clever in itself, but what impressed me more was that you can simply press the knob and the appliance will pause. Press it again, and it will start from where it left off. It may seem like a small thing, but I found it took away the hassle of turning down the speed when you want to pause the mix.</p><h2 id="3-double-whisk">3. Double whisk </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="meZBSQFjrR6TbefkE39twD" name="Ooni Halo Core stand mixer" alt="Ooni Halo Core stand mixer" src="https://cdn.mos.cms.futurecdn.net/meZBSQFjrR6TbefkE39twD.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I’ve never made butter before, but since my colleague made butter with the Halo Pro, I followed suit. I started by attaching the dual-geared whisk attachment — it even has a built-in gearbox for rapid whipping! </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3WnNedwWrnXHs2HuURMsVU" name="Ooni Halo Core stand mixer buttermilk separating" alt="Ooni Halo Core stand mixer buttermilk separating" src="https://cdn.mos.cms.futurecdn.net/3WnNedwWrnXHs2HuURMsVU.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>The whisk really came into its own, whipping the heavy cream into what looked like scrambled eggs until the fat and buttermilk separated. Apart from the speed, it’s fascinating to watch, and if you’ve never made butter before, I recommend giving it a go. </p><p>The whisk attachment proved that the Halo Core isn’t just good at making dough; it has a firm place in the kitchen for other baking tasks. </p><h3 class="article-body__section" id="section-ooni-halo-core-spiral-mixer-what-i-don-t-like"><span>Ooni Halo Core spiral mixer: What I don't like</span></h3><h2 id="it-wasn-39-t-great-at-mixing-cake-batter">It wasn't great at mixing cake batter</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ubR55uboGghEWXDKiHxLhD" name="Ooni Halo Core stand mixer" alt="Ooni Halo Core stand mixer" src="https://cdn.mos.cms.futurecdn.net/ubR55uboGghEWXDKiHxLhD.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>After the whisk's success, I was excited to try the angled silicone-edge beater to make an all-in-one cake batter. I love the silicone beater I’ve used with the KitchenAid Artisan Plus, as it swipes around the edge of the bowl, incorporating the whole mixture — so you don’t need to stop midway through to scrape the ingredients off the side.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kToJx2jJvD2CzLkw74AwjJ" name="Ooni Halo Core stand mixer cake batter" alt="Ooni Halo Core stand mixer making cake batter" src="https://cdn.mos.cms.futurecdn.net/kToJx2jJvD2CzLkw74AwjJ.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>The Halo Core did the same, but it seemed to struggle to incorporate the butter into the mixture, even though it was soft when I added it to the bowl. Then, when I raised the mixer’s head, I noticed a large clump of butter was still attached to the beater. </p><p>I’m going to keep testing its cake-making ability, since other reviews have given it more positive results. So keep an eye out for my full review.</p><p><strong>Over to you</strong></p><p>Are you thinking about buying an Ooni mixer, or have you already invested in one? I’d love to read your thoughts and experiences in the comments below.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchenaid-vs-cuisinart-which-stand-mixer-is-best">KitchenAid vs Cuisinart: Which stand mixer is best?</a></li><li><a href="https://www.tomsguide.com/home/smegs-newest-stand-mixer-gives-kitchenaid-a-run-for-its-money-heres-why-it-won-me-over">Smeg’s newest retro stand mixer gives KitchenAid a run for its money — here’s why it won me over</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/i-tested-oonis-new-rotating-stone-and-its-your-ticket-to-perfect-pizza-at-home">I tested Ooni's new Rotating Stone — and it's your ticket to perfect pizza at home</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Are You Storing Summer Foods in the Right Place? ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Summer brings backyard barbecues, farmers market hauls and refrigerators packed with everything from fresh produce to leftover cookout essentials. But while most people know milk belongs in the fridge and ice cream belongs in the freezer, many seasonal foods fall into a gray area.</p><p>Should tomatoes be refrigerated? Does bread stay fresher in the fridge or freezer? And how long can leftover hot dogs really last?</p><p>We all know that proper food storage can help ingredients stay fresh longer, reduce waste, and make meal planning easier. Yet crowded refrigerators, forgotten leftovers and misplaced ingredients remain a common challenge, especially at the hottest time of the year. </p><p>Here's a practical guide to where some of summer's most popular foods should actually be stored — and how <a href="https://sr.studiostack.com/c/link?l=2420846&s=2420844" target="_blank" rel="nofollow"><u>Bespoke AI Refrigerator Family Hub</u></a>’s smart technology can help keep track of what you have before they are forgotten.<sup>1</sup></p><h2 id="foods-that-belong-in-the-refrigerator">Foods that belong in the refrigerator</h2><p>When temperatures climb, the refrigerator becomes the most important food storage zone in the kitchen. While some items are obvious candidates, others may surprise you.</p><p><strong>Cut watermelon</strong></p><p>An uncut watermelon can stay at room temperature, but once sliced, it should be refrigerated immediately. Leaving cut watermelon out for extended periods can encourage bacterial growth and affect both texture and flavor.</p><p>Store slices in a sealed container or tightly wrapped on a shelf rather than in the refrigerator door, where the contents are exposed to outside temperature more frequently.</p><iframe src="https://content.jwplatform.com/players/uLhtjQ37.html" id="uLhtjQ37" title="Watermelon" width="1920" height="1080" frameborder="0" scrolling="auto" allowfullscreen></iframe><p><strong>Fresh corn on the cob</strong></p><p>One of summer's most popular vegetables is often stored incorrectly. Fresh corn starts losing sweetness shortly after harvest, and refrigeration helps slow that process.</p><p>For its flavor, keep corn in the refrigerator’s Crisper+ Drawer and cook it within a few days of purchase. And don’t forget, it should only be husked just before cooking, so it should go into the fridge in its natural case. Storing it at room temperature may cause sugars to convert to starch more quickly, and husking it before storage will lead to that same conversion of sugars to starch, as well as rapid moisture loss and oxidation.</p><iframe src="https://content.jwplatform.com/players/CiY6ZTuO.html" id="CiY6ZTuO" title="Corn On The Cob" width="1920" height="1080" frameborder="0" scrolling="auto" allowfullscreen></iframe><p><strong>Barbecue ribs and cooked leftovers</strong></p><p>Whether it's burgers, grilled chicken or ribs, cooked foods should be refrigerated within two hours of serving — and even sooner during extreme heat.</p><p>Leftovers often get pushed to the back of crowded shelves and forgotten. That's where organization becomes important. With Samsung Family Hub’s latest update, AI Vision<sup>2</sup> technology can identify and track many packaged foods and labeled containers placed inside compatible refrigerators, helping households see what's available and reducing the chances of leftovers being overlooked until it's too late.</p><iframe src="https://content.jwplatform.com/players/IJbpB7fv.html" id="IJbpB7fv" title="BBQ Ribs" width="1920" height="1080" frameborder="0" scrolling="auto" allowfullscreen></iframe><p><strong>Open condiments and sauces</strong></p><p>Barbecue sauce, mayonnaise-based dressings and many opened condiment containers belong in the refrigerator after opening. During cookout season, these items are often moved in and out of coolers and outdoor tables, making proper storage even more important.</p><p>For households juggling tens of bottles and jars, the updated AI Food Manager’s food list can be a game changer, since it helps prevent duplicate purchases and unnecessary waste.</p><iframe src="https://content.jwplatform.com/players/FJdfNgbC.html" id="FJdfNgbC" title="Jam" width="1536" height="1920" frameborder="0" scrolling="auto" allowfullscreen></iframe><p><strong>Berries</strong></p><p>Strawberries, blueberries and raspberries should be refrigerated soon after purchase. Summer berries are particularly delicate and can spoil quickly on the countertop.</p><p>To extend freshness, wait to wash berries until you're ready to eat them. Excess moisture can accelerate spoilage.</p><p><strong>Eggs</strong></p><p>Although eggs are displayed unrefrigerated in some countries, they should remain refrigerated in the United States. Consistent cold temperatures help maintain freshness, so, contrary to popular belief, they should be stored on a shelf inside the refrigerator, not in the door.</p><h2 id="foods-that-belong-in-the-freezer">Foods that belong in the freezer</h2><p>The freezer can do more than store ice cream and frozen vegetables. It can also help extend the life of foods that might otherwise go unused.</p><p><strong>Bread</strong></p><p>Many people automatically refrigerate bread, but that can actually make it stale faster.</p><p>If you plan to eat a loaf within a few days, keep it at room temperature. If not, freeze it. Bread freezes well and can be thawed slice by slice as needed.</p><p><strong>Avocados</strong></p><p>If you've ever watched an avocado go from perfectly ripe to overripe in what feels like a day, the freezer can help. Ripe avocados freeze surprisingly well and can be stored as halves, slices or mashed. </p><p>While they won't keep the same texture for salads or toast after thawing, they're ideal for smoothies, guacamole and baking.</p><p><strong>Excess herbs</strong></p><p>Fresh herbs such as basil, parsley and cilantro can spoil quickly. Freezing chopped herbs in ice cube trays with a small amount of water or olive oil creates ready-to-use portions for future meals.</p><h2 id="foods-that-should-stay-at-room-temperature">Foods that should stay at room temperature</h2><p>Not everything benefits from refrigeration. In fact, some foods lose flavor or texture when stored too cold.</p><p><strong>Tomatoes</strong></p><p>This is one of the most common storage mistakes.</p><p>Refrigeration can make tomatoes mealy and dull their flavor. Whole tomatoes are generally best kept on the countertop and away from direct sunlight until ripe and ready to eat.</p><p>Once cut, however, they should be refrigerated.</p><p><strong>Peaches, nectarines and plums</strong></p><p>Stone fruits continue ripening after purchase. Refrigerating them too early can slow that process and affect texture.</p><p>Leave them on the counter until ripe, then move them to the refrigerator if you need a few extra days before eating them.</p><p><strong>Onions</strong></p><p>Whole onions prefer a cool, dry and well-ventilated location rather than the refrigerator. Moisture can encourage spoilage and soften texture.</p><p>Just don't store them directly next to potatoes, which can shorten their shelf life.</p><h2 id="a-smarter-way-to-manage-summer-food-storage">A smarter way to manage summer food storage</h2><p>Knowing where foods belong is the first step toward keeping them fresher longer. The second is staying aware of what you already have.</p><p>As refrigerators fill up with produce, leftovers and barbecue essentials throughout the summer, it's easy for items to get lost or forgotten. As shown in the clips above, Samsung's upgraded AI Vision technology helps simplify food management by broadening its spectrum of recognized items – like your favorite breakfast jam and labeled food container with your leftovers – making it easier to keep track of ingredients and reduce unnecessary waste.</p><iframe src="https://content.jwplatform.com/players/xpkXFGCQ.html" id="xpkXFGCQ" title="AI Vision" width="1920" height="1080" frameborder="0" scrolling="auto" allowfullscreen></iframe><p>Learn more about Samsung’s Bespoke AI Refrigerator Family Hub <a href="https://sr.studiostack.com/c/link?l=2420848&s=2420844" target="_blank" rel="nofollow"><u>here</u></a>. </p><p><sup>1</sup> Features and availability may vary by model and market.<sup>2</sup> A Wi-Fi connection and a Samsung account are required. If a food item is not recognizable, it may be listed as an unknown item. AI Vision cannot identify or list any food items in the freezer. Recognition uses AI models that may be updated periodically to help improve performance. The user may need to manually check and adjust the list to ensure its accuracy.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/are-you-storing-summer-foods-in-the-right-place</link>
                                                                            <description>
                            <![CDATA[ Here are some tips for better storage ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">YSEzYXAA72Xst8gwCmD4EW</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/9AkR8iiQzLNMT5yXF5ahoe-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 08 Sep 2026 16:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Tech]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                                    <dc:creator><![CDATA[ Sponsored ]]></dc:creator>                                                                                                        <dc:description><![CDATA[ null ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/9AkR8iiQzLNMT5yXF5ahoe-1280-80.jpg">
                                                            <media:credit><![CDATA[Samsung]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Samsung Fridge in a kitchen]]></media:description>                                                            <media:text><![CDATA[Samsung Fridge in a kitchen]]></media:text>
                                <media:title type="plain"><![CDATA[Samsung Fridge in a kitchen]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/9AkR8iiQzLNMT5yXF5ahoe-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Summer brings backyard barbecues, farmers market hauls and refrigerators packed with everything from fresh produce to leftover cookout essentials. But while most people know milk belongs in the fridge and ice cream belongs in the freezer, many seasonal foods fall into a gray area.</p><p>Should tomatoes be refrigerated? Does bread stay fresher in the fridge or freezer? And how long can leftover hot dogs really last?</p><p>We all know that proper food storage can help ingredients stay fresh longer, reduce waste, and make meal planning easier. Yet crowded refrigerators, forgotten leftovers and misplaced ingredients remain a common challenge, especially at the hottest time of the year. </p><p>Here's a practical guide to where some of summer's most popular foods should actually be stored — and how <a href="https://sr.studiostack.com/c/link?l=2420846&s=2420844" target="_blank" rel="nofollow"><u>Bespoke AI Refrigerator Family Hub</u></a>’s smart technology can help keep track of what you have before they are forgotten.<sup>1</sup></p><h2 id="foods-that-belong-in-the-refrigerator">Foods that belong in the refrigerator</h2><p>When temperatures climb, the refrigerator becomes the most important food storage zone in the kitchen. While some items are obvious candidates, others may surprise you.</p><p><strong>Cut watermelon</strong></p><p>An uncut watermelon can stay at room temperature, but once sliced, it should be refrigerated immediately. Leaving cut watermelon out for extended periods can encourage bacterial growth and affect both texture and flavor.</p><p>Store slices in a sealed container or tightly wrapped on a shelf rather than in the refrigerator door, where the contents are exposed to outside temperature more frequently.</p><iframe src="https://content.jwplatform.com/players/uLhtjQ37.html" id="uLhtjQ37" title="Watermelon" width="1920" height="1080" frameborder="0" scrolling="auto" allowfullscreen></iframe><p><strong>Fresh corn on the cob</strong></p><p>One of summer's most popular vegetables is often stored incorrectly. Fresh corn starts losing sweetness shortly after harvest, and refrigeration helps slow that process.</p><p>For its flavor, keep corn in the refrigerator’s Crisper+ Drawer and cook it within a few days of purchase. And don’t forget, it should only be husked just before cooking, so it should go into the fridge in its natural case. Storing it at room temperature may cause sugars to convert to starch more quickly, and husking it before storage will lead to that same conversion of sugars to starch, as well as rapid moisture loss and oxidation.</p><iframe src="https://content.jwplatform.com/players/CiY6ZTuO.html" id="CiY6ZTuO" title="Corn On The Cob" width="1920" height="1080" frameborder="0" scrolling="auto" allowfullscreen></iframe><p><strong>Barbecue ribs and cooked leftovers</strong></p><p>Whether it's burgers, grilled chicken or ribs, cooked foods should be refrigerated within two hours of serving — and even sooner during extreme heat.</p><p>Leftovers often get pushed to the back of crowded shelves and forgotten. That's where organization becomes important. With Samsung Family Hub’s latest update, AI Vision<sup>2</sup> technology can identify and track many packaged foods and labeled containers placed inside compatible refrigerators, helping households see what's available and reducing the chances of leftovers being overlooked until it's too late.</p><iframe src="https://content.jwplatform.com/players/IJbpB7fv.html" id="IJbpB7fv" title="BBQ Ribs" width="1920" height="1080" frameborder="0" scrolling="auto" allowfullscreen></iframe><p><strong>Open condiments and sauces</strong></p><p>Barbecue sauce, mayonnaise-based dressings and many opened condiment containers belong in the refrigerator after opening. During cookout season, these items are often moved in and out of coolers and outdoor tables, making proper storage even more important.</p><p>For households juggling tens of bottles and jars, the updated AI Food Manager’s food list can be a game changer, since it helps prevent duplicate purchases and unnecessary waste.</p><iframe src="https://content.jwplatform.com/players/FJdfNgbC.html" id="FJdfNgbC" title="Jam" width="1536" height="1920" frameborder="0" scrolling="auto" allowfullscreen></iframe><p><strong>Berries</strong></p><p>Strawberries, blueberries and raspberries should be refrigerated soon after purchase. Summer berries are particularly delicate and can spoil quickly on the countertop.</p><p>To extend freshness, wait to wash berries until you're ready to eat them. Excess moisture can accelerate spoilage.</p><p><strong>Eggs</strong></p><p>Although eggs are displayed unrefrigerated in some countries, they should remain refrigerated in the United States. Consistent cold temperatures help maintain freshness, so, contrary to popular belief, they should be stored on a shelf inside the refrigerator, not in the door.</p><h2 id="foods-that-belong-in-the-freezer">Foods that belong in the freezer</h2><p>The freezer can do more than store ice cream and frozen vegetables. It can also help extend the life of foods that might otherwise go unused.</p><p><strong>Bread</strong></p><p>Many people automatically refrigerate bread, but that can actually make it stale faster.</p><p>If you plan to eat a loaf within a few days, keep it at room temperature. If not, freeze it. Bread freezes well and can be thawed slice by slice as needed.</p><p><strong>Avocados</strong></p><p>If you've ever watched an avocado go from perfectly ripe to overripe in what feels like a day, the freezer can help. Ripe avocados freeze surprisingly well and can be stored as halves, slices or mashed. </p><p>While they won't keep the same texture for salads or toast after thawing, they're ideal for smoothies, guacamole and baking.</p><p><strong>Excess herbs</strong></p><p>Fresh herbs such as basil, parsley and cilantro can spoil quickly. Freezing chopped herbs in ice cube trays with a small amount of water or olive oil creates ready-to-use portions for future meals.</p><h2 id="foods-that-should-stay-at-room-temperature">Foods that should stay at room temperature</h2><p>Not everything benefits from refrigeration. In fact, some foods lose flavor or texture when stored too cold.</p><p><strong>Tomatoes</strong></p><p>This is one of the most common storage mistakes.</p><p>Refrigeration can make tomatoes mealy and dull their flavor. Whole tomatoes are generally best kept on the countertop and away from direct sunlight until ripe and ready to eat.</p><p>Once cut, however, they should be refrigerated.</p><p><strong>Peaches, nectarines and plums</strong></p><p>Stone fruits continue ripening after purchase. Refrigerating them too early can slow that process and affect texture.</p><p>Leave them on the counter until ripe, then move them to the refrigerator if you need a few extra days before eating them.</p><p><strong>Onions</strong></p><p>Whole onions prefer a cool, dry and well-ventilated location rather than the refrigerator. Moisture can encourage spoilage and soften texture.</p><p>Just don't store them directly next to potatoes, which can shorten their shelf life.</p><h2 id="a-smarter-way-to-manage-summer-food-storage">A smarter way to manage summer food storage</h2><p>Knowing where foods belong is the first step toward keeping them fresher longer. The second is staying aware of what you already have.</p><p>As refrigerators fill up with produce, leftovers and barbecue essentials throughout the summer, it's easy for items to get lost or forgotten. As shown in the clips above, Samsung's upgraded AI Vision technology helps simplify food management by broadening its spectrum of recognized items – like your favorite breakfast jam and labeled food container with your leftovers – making it easier to keep track of ingredients and reduce unnecessary waste.</p><iframe src="https://content.jwplatform.com/players/xpkXFGCQ.html" id="xpkXFGCQ" title="AI Vision" width="1920" height="1080" frameborder="0" scrolling="auto" allowfullscreen></iframe><p>Learn more about Samsung’s Bespoke AI Refrigerator Family Hub <a href="https://sr.studiostack.com/c/link?l=2420848&s=2420844" target="_blank" rel="nofollow"><u>here</u></a>. </p><p><sup>1</sup> Features and availability may vary by model and market.<sup>2</sup> A Wi-Fi connection and a Samsung account are required. If a food item is not recognizable, it may be listed as an unknown item. AI Vision cannot identify or list any food items in the freezer. Recognition uses AI models that may be updated periodically to help improve performance. The user may need to manually check and adjust the list to ensure its accuracy.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Le Creuset just dropped an entire Star Trek collection — and I can't decide which item I like the most ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Le Creuset is an iconic kitchen brand. <a href="https://www.tomsguide.com/entertainment/movies/how-to-watch-the-star-trek-movies-in-order">Star Trek is an iconic franchise</a>. And what happens when you take two icons, put them in a bowl and mix them together? Well, a 12-piece kitchenware collection that'll turn your kitchen into a culinary command center.</p><p>To celebrate 60 years of "Star Trek", the cookware brand has lovingly crafted a limited-edition collection for fans of the franchise. Or, maybe you're just a fan of Spock (don't worry, there's a mug for that). </p><p>Christopher Scinto, Chief Marketing Officer at Le Creuset says: "With the Le Creuset x Star Trek Collection, we set out to commemorate some of the franchise's most beloved symbols with our signature craftsmanship and iconic colors and designs."</p><p>While there's 12 pieces in the collection that you can check out for a limited time only at Le Creuset stores, <a href="http://LeCreuset.com" target="_blank" rel="nofollow">LeCreuset.com</a>, and <a href="https://www.williams-sonoma.com/" target="_blank" rel="nofollow">Williams-Sonoma.com</a>, I've highlighted my five top picks below.</p><h2 id="what-39-s-in-my-basket">What's in my basket</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="VenG82ejhYRyErJC2Qj59Q" name="Le Creuset x Star Treck Collection 2" alt="The Le Creuset and Star Trek Collection" src="https://cdn.mos.cms.futurecdn.net/VenG82ejhYRyErJC2Qj59Q.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Le Creuset/CBS)</span></figcaption></figure><p>The full Le Creuset x "Star Trek" collection ranges from $35 to $500, but with 12 items there's hopefully something for every price range. Before we jump into my basket, I want to give a special mention to the Mini Cocottes for Balok, Gorn and Talosian. While they're not in my basket right now, they're certainly worth checking out if you're a fan.</p><div class="product"><a data-dimension112="433de4a6-aaba-11f1-b35b-5b7496d67c8f" data-action="Deal Block" data-label="Spock Mug" data-dimension48="Spock Mug" data-dimension25="$35" href="https://www.lecreuset.com/round-dutch-oven/21177US.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="CCp3WkA7DwRqzDscYBz35T" name="lecreuset_spockmug" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/CCp3WkA7DwRqzDscYBz35T.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.lecreuset.com/round-dutch-oven/21177US.html" target="_blank" rel="nofollow" data-dimension112="433de4a6-aaba-11f1-b35b-5b7496d67c8f" data-action="Deal Block" data-label="Spock Mug" data-dimension48="Spock Mug" data-dimension25="$35">Spock Mug: $35</a></strong><br>Le Creuset transforms the everyday mug handle into Spock's infamous ear in this unique design. While missing the usual gap for your fingers in the handle, it replaces it with the fun of gripping onto Spock's most defining feature.<a class="view-deal button" href="https://www.lecreuset.com/round-dutch-oven/21177US.html" target="_blank" rel="nofollow" data-dimension112="433de4a6-aaba-11f1-b35b-5b7496d67c8f" data-action="Deal Block" data-label="Spock Mug" data-dimension48="Spock Mug" data-dimension25="$35">View Deal</a></p></div><div class="product"><a data-dimension112="433de5f0-aaba-11f1-bbcc-932ffe9bbb44" data-action="Deal Block" data-label="Live Long and Prosper Spoon Rest" data-dimension48="Live Long and Prosper Spoon Rest" data-dimension25="$55" href="https://www.lecreuset.com/pepper-mill/44001.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="j3CT7XhbZUE9aXfNvK9Pm8" name="lecreuset_spoonrest" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/j3CT7XhbZUE9aXfNvK9Pm8.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.lecreuset.com/pepper-mill/44001.html" target="_blank" rel="nofollow" data-dimension112="433de5f0-aaba-11f1-bbcc-932ffe9bbb44" data-action="Deal Block" data-label="Live Long and Prosper Spoon Rest" data-dimension48="Live Long and Prosper Spoon Rest" data-dimension25="$55">Live Long and Prosper Spoon Rest: $55</a></strong><br>It wouldn't be <em>Star Trek</em> without the emblematic four-finger split. The Vulcan salute can become a kitchen staple instead taking on new life as a spoon rest that'll subtly tell your friends and family that you're a Trekkie – and a chef.<a class="view-deal button" href="https://www.lecreuset.com/pepper-mill/44001.html" target="_blank" rel="nofollow" data-dimension112="433de5f0-aaba-11f1-bbcc-932ffe9bbb44" data-action="Deal Block" data-label="Live Long and Prosper Spoon Rest" data-dimension48="Live Long and Prosper Spoon Rest" data-dimension25="$55">View Deal</a></p></div><div class="product"><a data-dimension112="433de514-aaba-11f1-840f-63fa391c57df" data-action="Deal Block" data-label="Communi-Grater" data-dimension48="Communi-Grater" data-dimension25="$68" href="https://www.lecreuset.com/signature-skillet/20182US.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="yM3UkLRCCQPiTnkkfysh8T" name="lecreuset_communigrater" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yM3UkLRCCQPiTnkkfysh8T.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.lecreuset.com/signature-skillet/20182US.html" target="_blank" rel="nofollow" data-dimension112="433de514-aaba-11f1-840f-63fa391c57df" data-action="Deal Block" data-label="Communi-Grater" data-dimension48="Communi-Grater" data-dimension25="$68">Communi-Grater: $68</a></strong><br>Le Creuset are known more for cookware than they are for kitchen accessories, but there's something extra special about this one. This greater plays on the Communicator with a quirky pun and a unique design to desire.<a class="view-deal button" href="https://www.lecreuset.com/signature-skillet/20182US.html" target="_blank" rel="nofollow" data-dimension112="433de514-aaba-11f1-840f-63fa391c57df" data-action="Deal Block" data-label="Communi-Grater" data-dimension48="Communi-Grater" data-dimension25="$68">View Deal</a></p></div><div class="product"><a data-dimension112="433de668-aaba-11f1-868d-fb080f159d20" data-action="Deal Block" data-label="Galileo Shuttlecraft Rectangular Casserole" data-dimension48="Galileo Shuttlecraft Rectangular Casserole" data-dimension25="$150" href="https://www.lecreuset.com/vancouver-mug/70317US.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="8QvQF6LU5t8Fto3vLjoDn8" name="lecreuset_galileocasserole" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/8QvQF6LU5t8Fto3vLjoDn8.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.lecreuset.com/vancouver-mug/70317US.html" target="_blank" rel="nofollow" data-dimension112="433de668-aaba-11f1-868d-fb080f159d20" data-action="Deal Block" data-label="Galileo Shuttlecraft Rectangular Casserole" data-dimension48="Galileo Shuttlecraft Rectangular Casserole" data-dimension25="$150">Galileo Shuttlecraft Rectangular Casserole: $150</a></strong><br>My favorite piece from the collection is this retro <em>Star Trek</em> twist on Le Creuset's iconic rectangular casserole dishes. A nod to the 1960s Galileo Shuttlecraft, it's equal parts nostalgia and cookware goodness.<a class="view-deal button" href="https://www.lecreuset.com/vancouver-mug/70317US.html" target="_blank" rel="nofollow" data-dimension112="433de668-aaba-11f1-868d-fb080f159d20" data-action="Deal Block" data-label="Galileo Shuttlecraft Rectangular Casserole" data-dimension48="Galileo Shuttlecraft Rectangular Casserole" data-dimension25="$150">View Deal</a></p></div><div class="product"><a data-dimension112="433de582-aaba-11f1-8881-0ddce702c4bc" data-action="Deal Block" data-label="Klingon Braiser with Klingon Trefoil Knob" data-dimension48="Klingon Braiser with Klingon Trefoil Knob" data-dimension25="$450" href="https://www.lecreuset.com/heritage-3-piece-rectangular-baking-dish-set/ST0098.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="SU5y49orWDCp43mFjBrG6T" name="lecreuset_klingonbraiser" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/SU5y49orWDCp43mFjBrG6T.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.lecreuset.com/heritage-3-piece-rectangular-baking-dish-set/ST0098.html" target="_blank" rel="nofollow" data-dimension112="433de582-aaba-11f1-8881-0ddce702c4bc" data-action="Deal Block" data-label="Klingon Braiser with Klingon Trefoil Knob" data-dimension48="Klingon Braiser with Klingon Trefoil Knob" data-dimension25="$450">Klingon Braiser with Klingon Trefoil Knob: $450</a></strong><br>And Le Creuset have given another iconic cookware item, the Braiser, a "Star Trek" twist. To commemorate 60 years, it has replaced the trefoil knob with a Klingon emblem whilst embossing a Klingon starship on the lid – all in a striking dark blue.<a class="view-deal button" href="https://www.lecreuset.com/heritage-3-piece-rectangular-baking-dish-set/ST0098.html" target="_blank" rel="nofollow" data-dimension112="433de582-aaba-11f1-8881-0ddce702c4bc" data-action="Deal Block" data-label="Klingon Braiser with Klingon Trefoil Knob" data-dimension48="Klingon Braiser with Klingon Trefoil Knob" data-dimension25="$450">View Deal</a></p></div><ul><li><a href="https://www.tomsguide.com/home/revolutionary-for-homemade-bread-why-i-cant-wait-to-try-le-creusets-new-oval-bread-oven">'Revolutionary for homemade bread': Why I can't wait to try Le Creuset's new oval bread oven</a></li><li><a href="https://www.tomsguide.com/home/cast-iron-skillet-vs-dutch-oven-which-is-better">Cast iron skillet vs Dutch oven — which is better?</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/what-is-a-dutch-oven-everything-you-need-to-know-about-this-kitchen-essential">What is a Dutch oven — everything you need to know about this kitchen essential</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/le-creuset-just-dropped-an-entire-star-trek-collection-and-i-cant-decide-which-item-i-like-the-most</link>
                                                                            <description>
                            <![CDATA[ In an unexpected collab, Le Creuset has teamed up with Paramount to celebrate 60 years of Star Trek with kitchenware that's sure to live long and prosper. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">bBZw6JfeqxmyuRJMyv53yD</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/wfxRUph98kwvpRwDGtveGQ-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 08 Sep 2026 13:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                                    <dc:creator><![CDATA[ Grace Dean ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oxXqkks7wgxZkPiyYY2n6H.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ null ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/wfxRUph98kwvpRwDGtveGQ-1280-80.jpg">
                                                            <media:credit><![CDATA[Le Creuset/CBS]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Le Creuset and Star Trek Collection]]></media:description>                                                            <media:text><![CDATA[The Le Creuset and Star Trek Collection]]></media:text>
                                <media:title type="plain"><![CDATA[The Le Creuset and Star Trek Collection]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/wfxRUph98kwvpRwDGtveGQ-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Le Creuset is an iconic kitchen brand. <a href="https://www.tomsguide.com/entertainment/movies/how-to-watch-the-star-trek-movies-in-order">Star Trek is an iconic franchise</a>. And what happens when you take two icons, put them in a bowl and mix them together? Well, a 12-piece kitchenware collection that'll turn your kitchen into a culinary command center.</p><p>To celebrate 60 years of "Star Trek", the cookware brand has lovingly crafted a limited-edition collection for fans of the franchise. Or, maybe you're just a fan of Spock (don't worry, there's a mug for that). </p><p>Christopher Scinto, Chief Marketing Officer at Le Creuset says: "With the Le Creuset x Star Trek Collection, we set out to commemorate some of the franchise's most beloved symbols with our signature craftsmanship and iconic colors and designs."</p><p>While there's 12 pieces in the collection that you can check out for a limited time only at Le Creuset stores, <a href="http://LeCreuset.com" target="_blank" rel="nofollow">LeCreuset.com</a>, and <a href="https://www.williams-sonoma.com/" target="_blank" rel="nofollow">Williams-Sonoma.com</a>, I've highlighted my five top picks below.</p><h2 id="what-39-s-in-my-basket">What's in my basket</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="VenG82ejhYRyErJC2Qj59Q" name="Le Creuset x Star Treck Collection 2" alt="The Le Creuset and Star Trek Collection" src="https://cdn.mos.cms.futurecdn.net/VenG82ejhYRyErJC2Qj59Q.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Le Creuset/CBS)</span></figcaption></figure><p>The full Le Creuset x "Star Trek" collection ranges from $35 to $500, but with 12 items there's hopefully something for every price range. Before we jump into my basket, I want to give a special mention to the Mini Cocottes for Balok, Gorn and Talosian. While they're not in my basket right now, they're certainly worth checking out if you're a fan.</p><div class="product"><a data-dimension112="433de4a6-aaba-11f1-b35b-5b7496d67c8f" data-action="Deal Block" data-label="Spock Mug" data-dimension48="Spock Mug" data-dimension25="$35" href="https://www.lecreuset.com/round-dutch-oven/21177US.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="CCp3WkA7DwRqzDscYBz35T" name="lecreuset_spockmug" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/CCp3WkA7DwRqzDscYBz35T.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.lecreuset.com/round-dutch-oven/21177US.html" target="_blank" rel="nofollow" data-dimension112="433de4a6-aaba-11f1-b35b-5b7496d67c8f" data-action="Deal Block" data-label="Spock Mug" data-dimension48="Spock Mug" data-dimension25="$35">Spock Mug: $35</a></strong><br>Le Creuset transforms the everyday mug handle into Spock's infamous ear in this unique design. While missing the usual gap for your fingers in the handle, it replaces it with the fun of gripping onto Spock's most defining feature.<a class="view-deal button" href="https://www.lecreuset.com/round-dutch-oven/21177US.html" target="_blank" rel="nofollow" data-dimension112="433de4a6-aaba-11f1-b35b-5b7496d67c8f" data-action="Deal Block" data-label="Spock Mug" data-dimension48="Spock Mug" data-dimension25="$35">View Deal</a></p></div><div class="product"><a data-dimension112="433de5f0-aaba-11f1-bbcc-932ffe9bbb44" data-action="Deal Block" data-label="Live Long and Prosper Spoon Rest" data-dimension48="Live Long and Prosper Spoon Rest" data-dimension25="$55" href="https://www.lecreuset.com/pepper-mill/44001.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="j3CT7XhbZUE9aXfNvK9Pm8" name="lecreuset_spoonrest" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/j3CT7XhbZUE9aXfNvK9Pm8.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.lecreuset.com/pepper-mill/44001.html" target="_blank" rel="nofollow" data-dimension112="433de5f0-aaba-11f1-bbcc-932ffe9bbb44" data-action="Deal Block" data-label="Live Long and Prosper Spoon Rest" data-dimension48="Live Long and Prosper Spoon Rest" data-dimension25="$55">Live Long and Prosper Spoon Rest: $55</a></strong><br>It wouldn't be <em>Star Trek</em> without the emblematic four-finger split. The Vulcan salute can become a kitchen staple instead taking on new life as a spoon rest that'll subtly tell your friends and family that you're a Trekkie – and a chef.<a class="view-deal button" href="https://www.lecreuset.com/pepper-mill/44001.html" target="_blank" rel="nofollow" data-dimension112="433de5f0-aaba-11f1-bbcc-932ffe9bbb44" data-action="Deal Block" data-label="Live Long and Prosper Spoon Rest" data-dimension48="Live Long and Prosper Spoon Rest" data-dimension25="$55">View Deal</a></p></div><div class="product"><a data-dimension112="433de514-aaba-11f1-840f-63fa391c57df" data-action="Deal Block" data-label="Communi-Grater" data-dimension48="Communi-Grater" data-dimension25="$68" href="https://www.lecreuset.com/signature-skillet/20182US.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="yM3UkLRCCQPiTnkkfysh8T" name="lecreuset_communigrater" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yM3UkLRCCQPiTnkkfysh8T.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.lecreuset.com/signature-skillet/20182US.html" target="_blank" rel="nofollow" data-dimension112="433de514-aaba-11f1-840f-63fa391c57df" data-action="Deal Block" data-label="Communi-Grater" data-dimension48="Communi-Grater" data-dimension25="$68">Communi-Grater: $68</a></strong><br>Le Creuset are known more for cookware than they are for kitchen accessories, but there's something extra special about this one. This greater plays on the Communicator with a quirky pun and a unique design to desire.<a class="view-deal button" href="https://www.lecreuset.com/signature-skillet/20182US.html" target="_blank" rel="nofollow" data-dimension112="433de514-aaba-11f1-840f-63fa391c57df" data-action="Deal Block" data-label="Communi-Grater" data-dimension48="Communi-Grater" data-dimension25="$68">View Deal</a></p></div><div class="product"><a data-dimension112="433de668-aaba-11f1-868d-fb080f159d20" data-action="Deal Block" data-label="Galileo Shuttlecraft Rectangular Casserole" data-dimension48="Galileo Shuttlecraft Rectangular Casserole" data-dimension25="$150" href="https://www.lecreuset.com/vancouver-mug/70317US.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="8QvQF6LU5t8Fto3vLjoDn8" name="lecreuset_galileocasserole" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/8QvQF6LU5t8Fto3vLjoDn8.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.lecreuset.com/vancouver-mug/70317US.html" target="_blank" rel="nofollow" data-dimension112="433de668-aaba-11f1-868d-fb080f159d20" data-action="Deal Block" data-label="Galileo Shuttlecraft Rectangular Casserole" data-dimension48="Galileo Shuttlecraft Rectangular Casserole" data-dimension25="$150">Galileo Shuttlecraft Rectangular Casserole: $150</a></strong><br>My favorite piece from the collection is this retro <em>Star Trek</em> twist on Le Creuset's iconic rectangular casserole dishes. A nod to the 1960s Galileo Shuttlecraft, it's equal parts nostalgia and cookware goodness.<a class="view-deal button" href="https://www.lecreuset.com/vancouver-mug/70317US.html" target="_blank" rel="nofollow" data-dimension112="433de668-aaba-11f1-868d-fb080f159d20" data-action="Deal Block" data-label="Galileo Shuttlecraft Rectangular Casserole" data-dimension48="Galileo Shuttlecraft Rectangular Casserole" data-dimension25="$150">View Deal</a></p></div><div class="product"><a data-dimension112="433de582-aaba-11f1-8881-0ddce702c4bc" data-action="Deal Block" data-label="Klingon Braiser with Klingon Trefoil Knob" data-dimension48="Klingon Braiser with Klingon Trefoil Knob" data-dimension25="$450" href="https://www.lecreuset.com/heritage-3-piece-rectangular-baking-dish-set/ST0098.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="SU5y49orWDCp43mFjBrG6T" name="lecreuset_klingonbraiser" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/SU5y49orWDCp43mFjBrG6T.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.lecreuset.com/heritage-3-piece-rectangular-baking-dish-set/ST0098.html" target="_blank" rel="nofollow" data-dimension112="433de582-aaba-11f1-8881-0ddce702c4bc" data-action="Deal Block" data-label="Klingon Braiser with Klingon Trefoil Knob" data-dimension48="Klingon Braiser with Klingon Trefoil Knob" data-dimension25="$450">Klingon Braiser with Klingon Trefoil Knob: $450</a></strong><br>And Le Creuset have given another iconic cookware item, the Braiser, a "Star Trek" twist. To commemorate 60 years, it has replaced the trefoil knob with a Klingon emblem whilst embossing a Klingon starship on the lid – all in a striking dark blue.<a class="view-deal button" href="https://www.lecreuset.com/heritage-3-piece-rectangular-baking-dish-set/ST0098.html" target="_blank" rel="nofollow" data-dimension112="433de582-aaba-11f1-8881-0ddce702c4bc" data-action="Deal Block" data-label="Klingon Braiser with Klingon Trefoil Knob" data-dimension48="Klingon Braiser with Klingon Trefoil Knob" data-dimension25="$450">View Deal</a></p></div><ul><li><a href="https://www.tomsguide.com/home/revolutionary-for-homemade-bread-why-i-cant-wait-to-try-le-creusets-new-oval-bread-oven">'Revolutionary for homemade bread': Why I can't wait to try Le Creuset's new oval bread oven</a></li><li><a href="https://www.tomsguide.com/home/cast-iron-skillet-vs-dutch-oven-which-is-better">Cast iron skillet vs Dutch oven — which is better?</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/what-is-a-dutch-oven-everything-you-need-to-know-about-this-kitchen-essential">What is a Dutch oven — everything you need to know about this kitchen essential</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I just tried the world’s first AI espresso machine — I'm a coffee snob, and it was surprisingly... good? ]]></title>
                                                                                                <dc:content><![CDATA[ <p>As a self-confessed coffee snob and director of Tom's Guide's <a href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, I'm always testing out new espresso products. At <a href="https://www.tomsguide.com/tag/ifa-2026">IFA 2026</a>, I tried nunc., an AI-assisted espresso machine. Yes, an AI espresso machine. </p><p>So what does the AI in nunc. actually do? Instead of having to manually dial in your beans, nunc. uses an NFC code to analyze pre-packaged, nunc.-branded beans (you can also use your own beans). Then, the machine analyzes humidity, heat, bean size, and basically every other variable to grind to the perfect particle size. As someone who loves espresso but <em>hates</em> dialing in (why does it take so long sometimes?), I was excited to try this out. But as someone skeptical of AI at best, I had my reservations. </p><p>I didn't want nunc. to take away the art of making coffee. That's so human and personalized that it's impossible for AI to copy. Luckily, though, nunc. proved me wrong. Here's what happened when I tried it.</p><h2 id="how-does-nunc-work">How does nunc. work?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kF8Kb6n2kBm3dGRVNXZRxn" name="nunc coffee 3" alt="the nunc. espresso machine with AI assisted dialling in" src="https://cdn.mos.cms.futurecdn.net/kF8Kb6n2kBm3dGRVNXZRxn.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As I mentioned above, every nunc.-branded bean hopper has an NFC code, exactly like a <a href="https://www.tomsguide.com/home/coffee-makers/nespresso-vertuo-up-review">Nespresso Vertuo</a> pod. The machine reads this NFC code to analyze the roast date, roast type, bean type, and processing type, and work out precisely how to grind those specific beans. Then, it analyzes the environment — heat, humidity — to finalize the optimal grind. </p><p>This means the grind size will change from morning to evening, or even hour to hour. If you have an espresso at 6 a.m., when the air in your house is dewy and humid, the grind size will be different from your 1 p.m. espresso, sun shining overhead, A/C running. </p><p>Of course, if you have the companion app, you can input information about any beans. </p><h2 id="how-is-nunc-different-from-a-normal-espresso-machine">How is nunc. different from a normal espresso machine?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="82wdT3Q2NSfh74EGRmjRun" name="nunc coffee 4" alt="the nunc. espresso machine with AI assisted dialling in" src="https://cdn.mos.cms.futurecdn.net/82wdT3Q2NSfh74EGRmjRun.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>In terms of functionality, nunc. works just like any other espresso machine. Think about it like this: it's almost like a superautomatic — <a href="https://www.tomsguide.com/home/coffee-makers/jura-c8-coffee-machine-review">Jura</a>, <a href="https://www.tomsguide.com/home/home-appliances/delonghi-rivelia-review">De'Longhi Rivelia</a> — but a totally manual machine. All it does <em>for you</em> is choose the ideal grind size. Honestly, dialling in beans is the worst part of espresso making for me, so I'm completely on board. </p><p>You tamp, insert the portafilter into the grouphead, and can pre-infuse the beans yourself. You can either steam the milk automatically (like the <a href="https://www.tomsguide.com/home/home-appliances/breville-sage-bambino-plus-review">Breville Bambino Plus</a>), or do it manually (any other manual espresso machine). </p><p>You dispose of the spent grounds yourself; you empty the drip tray yourself. The only part that is "AI" is the grinder. </p><h2 id="how-does-nunc-39-s-quot-ai-quot-coffee-taste">How does nunc.'s "AI" coffee taste?</h2><p>nunc. doesn't make "different" coffee. You won't have one sip and go, "Woah, I can totally taste the AI, dude!" It honestly tastes like a human barista was there every step of the way... because there was. <em>You </em>make the coffee. </p><p>Here's a shot of my oat flat white. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kxPZGdF7Adk8JTzFPLspmn" name="nunc coffee 1" alt="the nunc. espresso machine with AI assisted dialling in" src="https://cdn.mos.cms.futurecdn.net/kxPZGdF7Adk8JTzFPLspmn.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>It looks like a normal coffee. The taste was clean, polished, with a soft, dark-chocolate-like bitterness rounding it out. I wanted a little more fruit, acid, and funk — I like a funky coffee — but this was likely because I didn't choose my beans. </p><p>The milk texture was a little too bubbly for me; I like mine more velvety. Like any other espresso machine, though, you can adjust milk texture easily. Just don't aerate it as much.</p><p>I expected to dislike it because I'm extremely particular about my coffee, but this went down a treat. </p><h2 id="where-can-i-get-nunc">Where can I get nunc.?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="dhd4u4TNfBnADXJFWus9rn" name="nunc coffee 2" alt="the nunc. espresso machine with AI assisted dialling in" src="https://cdn.mos.cms.futurecdn.net/dhd4u4TNfBnADXJFWus9rn.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>So, now's the time for the bad news. As I'm writing this, nunc. is only available in Germany. If you happen to be in Germany, <a href="https://nunc.coffee/en/pages/nunc-espresso-machine-buy" target="_blank" rel="nofollow">nunc. costs €2,699</a>. For an extra €149, you get a grinder, the machine, a spring-loaded tamper, a milk jug, a brush, and a tamping mat. </p><p>That price equates to around $3,135, which makes this one of the most expensive home espresso machines I've ever seen. There's the $3,000 <a href="https://www.tomsguide.com/home/coffee-makers/breville-oracle-dual-boiler-review">Breville Dual Boiler</a>, but the nunc. out-prices many of the prosumer models like the Lelit Bianca ($2,999) and <a href="https://www.tomsguide.com/home/coffee-makers/fellow-espresso-series-1-review">Fellow Espresso Series 1</a> ($1,499).</p><p>nunc.'s representative informed me there is no release date for the U.S., U.K., E.U., Canada, Australia — basically any non-German markets. Hopefully, it won't be five years before we're seeing nunc. in our kitchens. I would love to play around with this machine myself. </p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchen-dining/canoly-c16-cold-press-juicer-review"><strong>I’ve been testing the Canoly C16 Cold Press Juicer — it somehow makes juicing complicated</strong></a></li><li><a href="https://www.tomsguide.com/home/home-appliances/i-just-watched-a-robot-vacuum-actually-climb-stairs-it-took-3-minutes-but-those-steps-were-sparkling-after"><strong>I just watched a robot vacuum actually climb stairs — it took 3 minutes but those steps were sparkling after</strong></a></li><li><a href="https://www.tomsguide.com/home/irobots-new-flagship-roomba-creates-an-airtight-seal-with-your-carpet-the-competition-should-be-worried"><strong>iRobot's new flagship Roomba creates an airtight seal with your carpet — the competition should be worried</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/i-just-tried-the-worlds-first-ai-espresso-machine-im-a-coffee-snob-and-it-was-surprisingly-good</link>
                                                                            <description>
                            <![CDATA[ I tried nunc., the world's first espresso machine that dials in your beans using AI. Is it actually good, or just a gimmick? ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">5V67MkSkyDoVQGsvB7tyXU</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/BXq2XNtTgqcS8bLM7WeWWV-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 04 Sep 2026 15:57:14 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[AI]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/BXq2XNtTgqcS8bLM7WeWWV-1280-80.jpg">
                                                            <media:credit><![CDATA[Erin Bashford]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[a photo of a coffee made on the nunc. ai espresso machine]]></media:description>                                                            <media:text><![CDATA[a photo of a coffee made on the nunc. ai espresso machine]]></media:text>
                                <media:title type="plain"><![CDATA[a photo of a coffee made on the nunc. ai espresso machine]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/BXq2XNtTgqcS8bLM7WeWWV-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As a self-confessed coffee snob and director of Tom's Guide's <a href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, I'm always testing out new espresso products. At <a href="https://www.tomsguide.com/tag/ifa-2026">IFA 2026</a>, I tried nunc., an AI-assisted espresso machine. Yes, an AI espresso machine. </p><p>So what does the AI in nunc. actually do? Instead of having to manually dial in your beans, nunc. uses an NFC code to analyze pre-packaged, nunc.-branded beans (you can also use your own beans). Then, the machine analyzes humidity, heat, bean size, and basically every other variable to grind to the perfect particle size. As someone who loves espresso but <em>hates</em> dialing in (why does it take so long sometimes?), I was excited to try this out. But as someone skeptical of AI at best, I had my reservations. </p><p>I didn't want nunc. to take away the art of making coffee. That's so human and personalized that it's impossible for AI to copy. Luckily, though, nunc. proved me wrong. Here's what happened when I tried it.</p><h2 id="how-does-nunc-work">How does nunc. work?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kF8Kb6n2kBm3dGRVNXZRxn" name="nunc coffee 3" alt="the nunc. espresso machine with AI assisted dialling in" src="https://cdn.mos.cms.futurecdn.net/kF8Kb6n2kBm3dGRVNXZRxn.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As I mentioned above, every nunc.-branded bean hopper has an NFC code, exactly like a <a href="https://www.tomsguide.com/home/coffee-makers/nespresso-vertuo-up-review">Nespresso Vertuo</a> pod. The machine reads this NFC code to analyze the roast date, roast type, bean type, and processing type, and work out precisely how to grind those specific beans. Then, it analyzes the environment — heat, humidity — to finalize the optimal grind. </p><p>This means the grind size will change from morning to evening, or even hour to hour. If you have an espresso at 6 a.m., when the air in your house is dewy and humid, the grind size will be different from your 1 p.m. espresso, sun shining overhead, A/C running. </p><p>Of course, if you have the companion app, you can input information about any beans. </p><h2 id="how-is-nunc-different-from-a-normal-espresso-machine">How is nunc. different from a normal espresso machine?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="82wdT3Q2NSfh74EGRmjRun" name="nunc coffee 4" alt="the nunc. espresso machine with AI assisted dialling in" src="https://cdn.mos.cms.futurecdn.net/82wdT3Q2NSfh74EGRmjRun.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>In terms of functionality, nunc. works just like any other espresso machine. Think about it like this: it's almost like a superautomatic — <a href="https://www.tomsguide.com/home/coffee-makers/jura-c8-coffee-machine-review">Jura</a>, <a href="https://www.tomsguide.com/home/home-appliances/delonghi-rivelia-review">De'Longhi Rivelia</a> — but a totally manual machine. All it does <em>for you</em> is choose the ideal grind size. Honestly, dialling in beans is the worst part of espresso making for me, so I'm completely on board. </p><p>You tamp, insert the portafilter into the grouphead, and can pre-infuse the beans yourself. You can either steam the milk automatically (like the <a href="https://www.tomsguide.com/home/home-appliances/breville-sage-bambino-plus-review">Breville Bambino Plus</a>), or do it manually (any other manual espresso machine). </p><p>You dispose of the spent grounds yourself; you empty the drip tray yourself. The only part that is "AI" is the grinder. </p><h2 id="how-does-nunc-39-s-quot-ai-quot-coffee-taste">How does nunc.'s "AI" coffee taste?</h2><p>nunc. doesn't make "different" coffee. You won't have one sip and go, "Woah, I can totally taste the AI, dude!" It honestly tastes like a human barista was there every step of the way... because there was. <em>You </em>make the coffee. </p><p>Here's a shot of my oat flat white. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kxPZGdF7Adk8JTzFPLspmn" name="nunc coffee 1" alt="the nunc. espresso machine with AI assisted dialling in" src="https://cdn.mos.cms.futurecdn.net/kxPZGdF7Adk8JTzFPLspmn.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>It looks like a normal coffee. The taste was clean, polished, with a soft, dark-chocolate-like bitterness rounding it out. I wanted a little more fruit, acid, and funk — I like a funky coffee — but this was likely because I didn't choose my beans. </p><p>The milk texture was a little too bubbly for me; I like mine more velvety. Like any other espresso machine, though, you can adjust milk texture easily. Just don't aerate it as much.</p><p>I expected to dislike it because I'm extremely particular about my coffee, but this went down a treat. </p><h2 id="where-can-i-get-nunc">Where can I get nunc.?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="dhd4u4TNfBnADXJFWus9rn" name="nunc coffee 2" alt="the nunc. espresso machine with AI assisted dialling in" src="https://cdn.mos.cms.futurecdn.net/dhd4u4TNfBnADXJFWus9rn.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>So, now's the time for the bad news. As I'm writing this, nunc. is only available in Germany. If you happen to be in Germany, <a href="https://nunc.coffee/en/pages/nunc-espresso-machine-buy" target="_blank" rel="nofollow">nunc. costs €2,699</a>. For an extra €149, you get a grinder, the machine, a spring-loaded tamper, a milk jug, a brush, and a tamping mat. </p><p>That price equates to around $3,135, which makes this one of the most expensive home espresso machines I've ever seen. There's the $3,000 <a href="https://www.tomsguide.com/home/coffee-makers/breville-oracle-dual-boiler-review">Breville Dual Boiler</a>, but the nunc. out-prices many of the prosumer models like the Lelit Bianca ($2,999) and <a href="https://www.tomsguide.com/home/coffee-makers/fellow-espresso-series-1-review">Fellow Espresso Series 1</a> ($1,499).</p><p>nunc.'s representative informed me there is no release date for the U.S., U.K., E.U., Canada, Australia — basically any non-German markets. Hopefully, it won't be five years before we're seeing nunc. in our kitchens. I would love to play around with this machine myself. </p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchen-dining/canoly-c16-cold-press-juicer-review"><strong>I’ve been testing the Canoly C16 Cold Press Juicer — it somehow makes juicing complicated</strong></a></li><li><a href="https://www.tomsguide.com/home/home-appliances/i-just-watched-a-robot-vacuum-actually-climb-stairs-it-took-3-minutes-but-those-steps-were-sparkling-after"><strong>I just watched a robot vacuum actually climb stairs — it took 3 minutes but those steps were sparkling after</strong></a></li><li><a href="https://www.tomsguide.com/home/irobots-new-flagship-roomba-creates-an-airtight-seal-with-your-carpet-the-competition-should-be-worried"><strong>iRobot's new flagship Roomba creates an airtight seal with your carpet — the competition should be worried</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve been testing the Canoly C16 Cold Press Juicer — it somehow makes juicing complicated ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Canoly C16 Cold Press Juicer promises a convenient way to make juice, nut milk and sorbet. But if anything, it proved to me once and for all that if you want a smooth-sailing juicing experience, you need to spend a little more money. </p><p>The machine is very quiet when operating and has a large hopper, meaning you can load up your ingredients in bulk. It’s also very simple to use with just one switch to control everything. However, I’m not sure any of that makes it worth the hassle. </p><p>It regularly overheats, is incredibly difficult to clean, and clogs frequently. To find out more about my experience with the machine, you can keep reading this full Canoly C16 Cold Press Juicer review.</p><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-specs"><span>Canoly C16 Cold Press Juicer review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.canoly.com/products/c16-cold-press-juicer" target="_blank" rel="nofollow">$299</a> / <a href="https://www.amazon.co.uk/Canoly-Press-Juicer-Machines-150mm/dp/B0H1KQNVL3" target="_blank" rel="nofollow">£93</a></p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>17.3 x 9.7 x 6.7 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>13.2 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Functions</strong></p></td><td  ><p>Juice, Nut milk, sorbet</p></td></tr><tr><td class="firstcol " ><p><strong>Material</strong></p></td><td  ><p>PC and stainless steel</p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>90 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Power</strong></p></td><td  ><p>250W</p></td></tr><tr><td class="firstcol " ><p><strong>Dishwasher safe</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Warranty</strong></p></td><td  ><p>1-year</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-price-availability"><span>Canoly C16 Cold Press Juicer review: Price & availability</span></h2><p>The Canoly C16 Cold Press Juicer is available to purchase in the U.S. for <a href="https://www.canoly.com/products/c16-cold-press-juicer" target="_blank" rel="nofollow">$299 directly from the Canoly online store</a>, but it’s often on sale for as low as $199. In the U.K., you can pick it up for <a href="https://www.amazon.co.uk/Canoly-Press-Juicer-Machines-150mm/dp/B0H1KQNVL3" target="_blank" rel="nofollow">£93 from Amazon</a>. </p><p>This is very cheap for a cold press juicer, especially a 3-in-1 machine, as you need a <em>lot</em> of power to make nut milk. For example, the <a href="https://www.tomsguide.com/home/home-appliances/nama-c2-cold-press-juicer-and-blender-review">Nama C2 Cold Press Juicer and Blender</a> can make nut milk with ease, but it retails for $800 — ouch!</p><p>Primarily, the Canoly is a juicer and sits in the market alongside the <a href="https://www.tomsguide.com/home/home-appliances/amzchef-cold-press-juicer-zm1902-review">AMZCHEF Cold Press Juicer</a>, which is priced at $199. This juicer is the same size as the Canoly but is far more controlled and user-friendly whilst remaining a budget option. </p><p>If you’re short on space, you could pick up the Ninja NeverClog Cold Press Juicer for $149, but it’s not the strongest and the process is very fiddly, so I would recommend a model with a hopper, but not the Canoly. Instead, the AMZCHEF would be the best option. </p><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-design"><span>Canoly C16 Cold Press Juicer review: Design </span></h2><p>The Canoly C16 Cold Press Juicer looks like any other juicer with a hopper. It’s striking and catches your attention immediately because of its size and almost futuristic design — the handle’s shape is almost spaceship-like — although it doesn’t have a flurry of touchscreen buttons like the <a href="https://www.tomsguide.com/home/home-appliances/tribest-slowstar-ai-vertical-hopper-juicer-review">Tribest Slowstar AI Vertical Hopper Juicer</a> ($599).</p><p>The machine measures 17.3 x 9.7 x 6.7 inches, so it’s very tall. It didn’t fit under my kitchen cabinets, so storing it on the countertop wasn't an option. However, it’s easy to dismantle so it can be stored away in the cupboard. The Canoly C16 weighs 13.2 pounds, and I was able to carry it around easily while setting it up. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iMvfwrEcDn64YsuhH2G4iV" name="Canoly C16 Cold Press Juicer" alt="Canoly C16 Cold Press Juicer photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/iMvfwrEcDn64YsuhH2G4iV.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Inside the hopper, there’s a pre-cutting blade, and this made light work of tough ingredients, so I didn’t have to spend time chopping before adding them to the hopper. That said, the small feed chute on top measures six inches in diameter, so if you want to continuously feed ingredients, you’ll need to spend time chopping. I’d recommend simply lifting the lid and adding ingredients that way.  </p><p>There are two different filters with the machine, so you can make milk, sorbet or juice, meaning you're not limited in what you can do. There are two spouts on either side of the machine. On the left is the pulp and on the right is the juice. You can open and close these as needed. I didn’t experience any drips or spills, so I was happy with these.  </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="D976oddxaPAUkJ9pca6fMW" name="Canoly C16 Cold Press Juicer" alt="Canoly C16 Cold Press Juicer photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/D976oddxaPAUkJ9pca6fMW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The juicer comes with two containers, one for collecting pulp and one for collecting juice. The juice container has a small lip to reduce spills and measurements denoted on the side. My only issue is that the measurements are very faint, and I found myself searching around the container for them. I found it easier to use my own measuring jug to see exactly how much liquid I was getting. </p><h3 id="controls">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4pB6fDsGKRTvKRSniNHKBW" name="Canoly C16 Cold Press Juicer" alt="Canoly C16 Cold Press Juicer photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/4pB6fDsGKRTvKRSniNHKBW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Canoly C16 couldn’t be easier to use because it has one switch. Flip it one way for juice and the other for reverse. Annoyingly, this button is placed at the back of the juicer, so it’s a bit out of the way when you’re using it. When the hopper is open, the juicer stops for safety, like any other juicer. </p><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-performance"><span>Canoly C16 Cold Press Juicer review: Performance</span></h2><p>The Canoly C16 Cold Press Juicer can do a lot. It’s a 3-in-1 machine, so it can make juice, nut milk, and sorbet. It sounds good on the packaging, but using it let me down. During operation, the machine was continuously overheating, despite the Canoly website stating it can handle 30 minutes of continuous operation. It also smells like burnt plastic after about 10 minutes of use. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iwot5zrj4w3TRGMiFiuqHW" name="Canoly C16 Cold Press Juicer" alt="Canoly C16 Cold Press Juicer photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/iwot5zrj4w3TRGMiFiuqHW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The machine clogs often, so I had to reverse the juicing a couple of times, and it is extremely hard to clean as the pulp gets stuck throughout the machine rather than being pushed out. That said, it’s very quiet, operating at around 40 decibels thanks to the brushless motor. </p><p>To get an idea of what the C16 is capable of, I carried out my usual juicing tests and made nut milk and sorbet. </p><p><strong>Apple juice:</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8hbjwhZ9YaUxDD9xxKVMCW" name="Canoly C16 Cold Press Juicer" alt="apple juice made using the Canoly C16 Cold Press Juicer" src="https://cdn.mos.cms.futurecdn.net/8hbjwhZ9YaUxDD9xxKVMCW.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Firstly, I juiced four apples, which gave me 700ml of juice. I cut the apples in half and put them straight into the hopper, which could fit two and a half apples at a time. </p><p>I was surprised by how long the apples took to juice. It looked as though they were being mulched in the hopper by the chopping blade. After all of the juice was extracted, there was still a lot of the apple stuck to the side walls of the hopper. </p><p>The fiber took a long time to come out, but it was mostly dry, indicating that most of the moisture had been extracted from the fruit. There was foam on the top of the apple juice but not much, meaning there wasn’t much air in the machine. This meant the juice had a sweeter taste, which is what you want. The juice was a little bitty, but felt mostly smooth in my mouth while drinking. </p><p><strong>Orange juice:</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="n445zcTkbAFeQnpm4QoeKW" name="Canoly C16 Cold Press Juicer" alt="orange juice made using the Canoly C16 Cold Press Juicer" src="https://cdn.mos.cms.futurecdn.net/n445zcTkbAFeQnpm4QoeKW.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Next up, I made a batch of orange juice using four oranges, which gave me a yield of 400ml. Before putting them in the hopper, they were peeled to avoid the bitter taste of the pith making its way into the final product. </p><p>This was a lot faster than the apple, but the pulp that came out of the machine was very moist, so it didn’t do a great job of getting all the juice out. The juice itself was thick in texture but not exactly bitty. </p><p>After juicing the four oranges without the peel, the juicer overheated. I left it for around half an hour and it came back to life, but four oranges should be a very easy ask. </p><p><strong>Carrot and Celery juice:</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="KhrQ3NTYB9RSZfVNmJjYQW" name="Canoly C16 Cold Press Juicer" alt="carrot and celery made using the Canoly C16 Cold Press Juicer" src="https://cdn.mos.cms.futurecdn.net/KhrQ3NTYB9RSZfVNmJjYQW.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I then made carrot and celery juice. I juiced 2.2 pounds of carrots and a whole bunch of celery, which gave me 500ml of juice. The chopping blade in the hopper was able to easily break down the carrot into manageable pieces for the auger, which was impressive to watch, but it was so strong that some small pieces of carrots came flying out at me through the six-inch feed chute. </p><p>The ingredients moved through the hopper quickly, but the celery stalks became very tangled, and there was a lot of carrot pulp remaining throughout the machine. A lot of the pulp made it into the juice too, unfortunately resulting in a very thick texture. </p><p><strong>Cashew milk:</strong></p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/pXLDiYvri8ZzSecZm4tPUW.jpg" alt="cashew nut milk made using the Canoly C16 Cold Press Juicer" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZXrSSj4uDgSXpwNKsiUafW.jpg" alt="cashew nut milk made using the Canoly C16 Cold Press Juicer" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/bn9Bht7urDYXPEFprBnhXW.jpg" alt="cashew nut milk made using the Canoly C16 Cold Press Juicer" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>Moving on from classic juices, I made some nut milk. I opted for cashews and soaked two cups in water overnight in the refrigerator. After rinsing the nuts the next day, I slowly fed them into the juicer with filtered water. </p><p>As the milk was coming out of the juicer, I used the included sieve over the cup to catch any rogue lumps… and there were a lot. That said, the milk tasted good. I would just recommend using a cheesecloth to strain the milk after juicing to ensure there are no lumps. </p><p>The pulp was effectively pushed out of the machine, although there wasn’t much of it. But when I cleaned the juicer, all of the pulp was stuck in the hollow auger. This was annoying to clean out. </p><p><strong>Sorbet:</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="QP97YbpWKMVoMDwn9FiupW" name="Canoly C16 Cold Press Juicer" alt="berry sorbet made using the Canoly C16 Cold Press Juicer" src="https://cdn.mos.cms.futurecdn.net/QP97YbpWKMVoMDwn9FiupW.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Finally, I made sorbet using the included sorbet strainer. I added a mixture of frozen berries and cherries to the machine and simply pressed the on button. </p><p>The machine stopped a few times and had to be reversed. This isn’t something I’ve had to deal with when using any other juicer, but it just meant I had to be on hand to hit the reverse button when needed. </p><p>It quickly made spirals of sorbet, giving me a sweet treat that was ready in minutes. This clean-up was also a lot more straightforward than the juices with their messy pulp. </p><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-cleaning"><span>Canoly C16 Cold Press Juicer review: Cleaning</span></h2><p>The Canoly C16 Cold Press Juicer is a nightmare to maintain. Juicers are notoriously difficult to keep clean because all of the small pieces of fruit and vegetables are being pushed through small gaps to get the most liquid possible. So everything gets trapped. But the Canoly is especially bad. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YBTGyEyzuu6vQxE6h47SpV" name="Canoly C16 Cold Press Juicer" alt="Canoly C16 Cold Press Juicer photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/YBTGyEyzuu6vQxE6h47SpV.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>All of the parts are dishwasher safe apart from the motor base. Before I loaded it into the dishwasher, I rinsed off as much pulp as possible, but there was still some residue after the wash cycle. I went in with the provided cleaning brush to get the final bits out, but it took me about half an hour of scrubbing to be happy with the cleanliness of the machine. Tiny bits of pulp get stuck in the smallest crevices you’ve ever seen — nightmare.</p><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-verdict"><span>Canoly C16 Cold Press Juicer review: Verdict</span></h2><p>The Canoly C16 Cold Press is a 3-in-1 machine, so I was able to make milk, sorbet and juice. It’s very useful that all of the necessary attachments are included when you purchase the juicer. It’s very quiet when operating too, so it doesn’t cause too much of a commotion in the home. </p><p>The large hopper makes it easy to juice in batches if you need to, and it couldn’t be easier to use, with just one switch that controls everything. </p><p>However, this juice wastes a lot of your time through cleaning and waiting for it to stop overheating. Cleaning is incredibly difficult, with pulp getting caught up in every crevice you can think of, the machine frequently clogs, meaning you have to be on hand to hit the reverse switch. And it overheats when juicing fruits that should be easy, like oranges. </p><p>If you seriously want to get into juicing, I would recommend spending more money on a Nama juicer like the <a href="https://www.tomsguide.com/home/nama-j3-cold-press-juicer-review">Nama J3</a>.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/canoly-c16-cold-press-juicer-review</link>
                                                                            <description>
                            <![CDATA[ The Canoly C16 Cold Press Juicer is a cheap option, but overheating and clogging may put you off. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">iaJs3iNAsdcyFuNFGmN655</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/xtMhjtvX8jF75vgULxzLXV-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 04 Sep 2026 14:05:52 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ ashley.thieme@futurenet.com (Ashley Thieme) ]]></author>                    <dc:creator><![CDATA[ Ashley Thieme ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3AWovHjApwuNrSGRS6WBcL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ashley Thieme is a staff writer on the Reviews team at Tom’s Guide where she tests out the latest tech so you can know what’s going to be worth your time and money. She has a master’s degree in Magazine Journalism and a bachelor’s degree in Journalism, Media and Sociology from Cardiff University. She has bylines in titles including Women’s Health UK, sharing the latest health and wellness news stories, and Virgin Radio UK, providing the latest entertainment news and working on celebrity interviews. She has experience reporting on a variety of topics including music, literature, motorsport, entertainment and health. In previously published work, she has reviewed live music events, books, and wellness products. She values the importance of tech enhancing your life rather than taking over, and as a music fanatic, she is always looking for the best way to listen to new music releases. Discovering the top audio equipment that enhances sound quality and provides optimum comfort is what Ashley does best. In her spare time, Ashley enjoys hitting her reading goals on Goodreads by getting into the latest novels with a cup of tea as well as getting out in the Welsh mountains for a good hike on the weekend.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/xtMhjtvX8jF75vgULxzLXV-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Canoly C16 Cold Press Juicer photographed in front of a blue background]]></media:description>                                                            <media:text><![CDATA[Canoly C16 Cold Press Juicer photographed in front of a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[Canoly C16 Cold Press Juicer photographed in front of a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/xtMhjtvX8jF75vgULxzLXV-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Canoly C16 Cold Press Juicer promises a convenient way to make juice, nut milk and sorbet. But if anything, it proved to me once and for all that if you want a smooth-sailing juicing experience, you need to spend a little more money. </p><p>The machine is very quiet when operating and has a large hopper, meaning you can load up your ingredients in bulk. It’s also very simple to use with just one switch to control everything. However, I’m not sure any of that makes it worth the hassle. </p><p>It regularly overheats, is incredibly difficult to clean, and clogs frequently. To find out more about my experience with the machine, you can keep reading this full Canoly C16 Cold Press Juicer review.</p><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-specs"><span>Canoly C16 Cold Press Juicer review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.canoly.com/products/c16-cold-press-juicer" target="_blank" rel="nofollow">$299</a> / <a href="https://www.amazon.co.uk/Canoly-Press-Juicer-Machines-150mm/dp/B0H1KQNVL3" target="_blank" rel="nofollow">£93</a></p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>17.3 x 9.7 x 6.7 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>13.2 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Functions</strong></p></td><td  ><p>Juice, Nut milk, sorbet</p></td></tr><tr><td class="firstcol " ><p><strong>Material</strong></p></td><td  ><p>PC and stainless steel</p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>90 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Power</strong></p></td><td  ><p>250W</p></td></tr><tr><td class="firstcol " ><p><strong>Dishwasher safe</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Warranty</strong></p></td><td  ><p>1-year</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-price-availability"><span>Canoly C16 Cold Press Juicer review: Price & availability</span></h2><p>The Canoly C16 Cold Press Juicer is available to purchase in the U.S. for <a href="https://www.canoly.com/products/c16-cold-press-juicer" target="_blank" rel="nofollow">$299 directly from the Canoly online store</a>, but it’s often on sale for as low as $199. In the U.K., you can pick it up for <a href="https://www.amazon.co.uk/Canoly-Press-Juicer-Machines-150mm/dp/B0H1KQNVL3" target="_blank" rel="nofollow">£93 from Amazon</a>. </p><p>This is very cheap for a cold press juicer, especially a 3-in-1 machine, as you need a <em>lot</em> of power to make nut milk. For example, the <a href="https://www.tomsguide.com/home/home-appliances/nama-c2-cold-press-juicer-and-blender-review">Nama C2 Cold Press Juicer and Blender</a> can make nut milk with ease, but it retails for $800 — ouch!</p><p>Primarily, the Canoly is a juicer and sits in the market alongside the <a href="https://www.tomsguide.com/home/home-appliances/amzchef-cold-press-juicer-zm1902-review">AMZCHEF Cold Press Juicer</a>, which is priced at $199. This juicer is the same size as the Canoly but is far more controlled and user-friendly whilst remaining a budget option. </p><p>If you’re short on space, you could pick up the Ninja NeverClog Cold Press Juicer for $149, but it’s not the strongest and the process is very fiddly, so I would recommend a model with a hopper, but not the Canoly. Instead, the AMZCHEF would be the best option. </p><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-design"><span>Canoly C16 Cold Press Juicer review: Design </span></h2><p>The Canoly C16 Cold Press Juicer looks like any other juicer with a hopper. It’s striking and catches your attention immediately because of its size and almost futuristic design — the handle’s shape is almost spaceship-like — although it doesn’t have a flurry of touchscreen buttons like the <a href="https://www.tomsguide.com/home/home-appliances/tribest-slowstar-ai-vertical-hopper-juicer-review">Tribest Slowstar AI Vertical Hopper Juicer</a> ($599).</p><p>The machine measures 17.3 x 9.7 x 6.7 inches, so it’s very tall. It didn’t fit under my kitchen cabinets, so storing it on the countertop wasn't an option. However, it’s easy to dismantle so it can be stored away in the cupboard. The Canoly C16 weighs 13.2 pounds, and I was able to carry it around easily while setting it up. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iMvfwrEcDn64YsuhH2G4iV" name="Canoly C16 Cold Press Juicer" alt="Canoly C16 Cold Press Juicer photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/iMvfwrEcDn64YsuhH2G4iV.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Inside the hopper, there’s a pre-cutting blade, and this made light work of tough ingredients, so I didn’t have to spend time chopping before adding them to the hopper. That said, the small feed chute on top measures six inches in diameter, so if you want to continuously feed ingredients, you’ll need to spend time chopping. I’d recommend simply lifting the lid and adding ingredients that way.  </p><p>There are two different filters with the machine, so you can make milk, sorbet or juice, meaning you're not limited in what you can do. There are two spouts on either side of the machine. On the left is the pulp and on the right is the juice. You can open and close these as needed. I didn’t experience any drips or spills, so I was happy with these.  </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="D976oddxaPAUkJ9pca6fMW" name="Canoly C16 Cold Press Juicer" alt="Canoly C16 Cold Press Juicer photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/D976oddxaPAUkJ9pca6fMW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The juicer comes with two containers, one for collecting pulp and one for collecting juice. The juice container has a small lip to reduce spills and measurements denoted on the side. My only issue is that the measurements are very faint, and I found myself searching around the container for them. I found it easier to use my own measuring jug to see exactly how much liquid I was getting. </p><h3 id="controls">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4pB6fDsGKRTvKRSniNHKBW" name="Canoly C16 Cold Press Juicer" alt="Canoly C16 Cold Press Juicer photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/4pB6fDsGKRTvKRSniNHKBW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Canoly C16 couldn’t be easier to use because it has one switch. Flip it one way for juice and the other for reverse. Annoyingly, this button is placed at the back of the juicer, so it’s a bit out of the way when you’re using it. When the hopper is open, the juicer stops for safety, like any other juicer. </p><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-performance"><span>Canoly C16 Cold Press Juicer review: Performance</span></h2><p>The Canoly C16 Cold Press Juicer can do a lot. It’s a 3-in-1 machine, so it can make juice, nut milk, and sorbet. It sounds good on the packaging, but using it let me down. During operation, the machine was continuously overheating, despite the Canoly website stating it can handle 30 minutes of continuous operation. It also smells like burnt plastic after about 10 minutes of use. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iwot5zrj4w3TRGMiFiuqHW" name="Canoly C16 Cold Press Juicer" alt="Canoly C16 Cold Press Juicer photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/iwot5zrj4w3TRGMiFiuqHW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The machine clogs often, so I had to reverse the juicing a couple of times, and it is extremely hard to clean as the pulp gets stuck throughout the machine rather than being pushed out. That said, it’s very quiet, operating at around 40 decibels thanks to the brushless motor. </p><p>To get an idea of what the C16 is capable of, I carried out my usual juicing tests and made nut milk and sorbet. </p><p><strong>Apple juice:</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8hbjwhZ9YaUxDD9xxKVMCW" name="Canoly C16 Cold Press Juicer" alt="apple juice made using the Canoly C16 Cold Press Juicer" src="https://cdn.mos.cms.futurecdn.net/8hbjwhZ9YaUxDD9xxKVMCW.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Firstly, I juiced four apples, which gave me 700ml of juice. I cut the apples in half and put them straight into the hopper, which could fit two and a half apples at a time. </p><p>I was surprised by how long the apples took to juice. It looked as though they were being mulched in the hopper by the chopping blade. After all of the juice was extracted, there was still a lot of the apple stuck to the side walls of the hopper. </p><p>The fiber took a long time to come out, but it was mostly dry, indicating that most of the moisture had been extracted from the fruit. There was foam on the top of the apple juice but not much, meaning there wasn’t much air in the machine. This meant the juice had a sweeter taste, which is what you want. The juice was a little bitty, but felt mostly smooth in my mouth while drinking. </p><p><strong>Orange juice:</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="n445zcTkbAFeQnpm4QoeKW" name="Canoly C16 Cold Press Juicer" alt="orange juice made using the Canoly C16 Cold Press Juicer" src="https://cdn.mos.cms.futurecdn.net/n445zcTkbAFeQnpm4QoeKW.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Next up, I made a batch of orange juice using four oranges, which gave me a yield of 400ml. Before putting them in the hopper, they were peeled to avoid the bitter taste of the pith making its way into the final product. </p><p>This was a lot faster than the apple, but the pulp that came out of the machine was very moist, so it didn’t do a great job of getting all the juice out. The juice itself was thick in texture but not exactly bitty. </p><p>After juicing the four oranges without the peel, the juicer overheated. I left it for around half an hour and it came back to life, but four oranges should be a very easy ask. </p><p><strong>Carrot and Celery juice:</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="KhrQ3NTYB9RSZfVNmJjYQW" name="Canoly C16 Cold Press Juicer" alt="carrot and celery made using the Canoly C16 Cold Press Juicer" src="https://cdn.mos.cms.futurecdn.net/KhrQ3NTYB9RSZfVNmJjYQW.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I then made carrot and celery juice. I juiced 2.2 pounds of carrots and a whole bunch of celery, which gave me 500ml of juice. The chopping blade in the hopper was able to easily break down the carrot into manageable pieces for the auger, which was impressive to watch, but it was so strong that some small pieces of carrots came flying out at me through the six-inch feed chute. </p><p>The ingredients moved through the hopper quickly, but the celery stalks became very tangled, and there was a lot of carrot pulp remaining throughout the machine. A lot of the pulp made it into the juice too, unfortunately resulting in a very thick texture. </p><p><strong>Cashew milk:</strong></p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/pXLDiYvri8ZzSecZm4tPUW.jpg" alt="cashew nut milk made using the Canoly C16 Cold Press Juicer" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZXrSSj4uDgSXpwNKsiUafW.jpg" alt="cashew nut milk made using the Canoly C16 Cold Press Juicer" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/bn9Bht7urDYXPEFprBnhXW.jpg" alt="cashew nut milk made using the Canoly C16 Cold Press Juicer" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>Moving on from classic juices, I made some nut milk. I opted for cashews and soaked two cups in water overnight in the refrigerator. After rinsing the nuts the next day, I slowly fed them into the juicer with filtered water. </p><p>As the milk was coming out of the juicer, I used the included sieve over the cup to catch any rogue lumps… and there were a lot. That said, the milk tasted good. I would just recommend using a cheesecloth to strain the milk after juicing to ensure there are no lumps. </p><p>The pulp was effectively pushed out of the machine, although there wasn’t much of it. But when I cleaned the juicer, all of the pulp was stuck in the hollow auger. This was annoying to clean out. </p><p><strong>Sorbet:</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="QP97YbpWKMVoMDwn9FiupW" name="Canoly C16 Cold Press Juicer" alt="berry sorbet made using the Canoly C16 Cold Press Juicer" src="https://cdn.mos.cms.futurecdn.net/QP97YbpWKMVoMDwn9FiupW.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Finally, I made sorbet using the included sorbet strainer. I added a mixture of frozen berries and cherries to the machine and simply pressed the on button. </p><p>The machine stopped a few times and had to be reversed. This isn’t something I’ve had to deal with when using any other juicer, but it just meant I had to be on hand to hit the reverse button when needed. </p><p>It quickly made spirals of sorbet, giving me a sweet treat that was ready in minutes. This clean-up was also a lot more straightforward than the juices with their messy pulp. </p><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-cleaning"><span>Canoly C16 Cold Press Juicer review: Cleaning</span></h2><p>The Canoly C16 Cold Press Juicer is a nightmare to maintain. Juicers are notoriously difficult to keep clean because all of the small pieces of fruit and vegetables are being pushed through small gaps to get the most liquid possible. So everything gets trapped. But the Canoly is especially bad. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YBTGyEyzuu6vQxE6h47SpV" name="Canoly C16 Cold Press Juicer" alt="Canoly C16 Cold Press Juicer photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/YBTGyEyzuu6vQxE6h47SpV.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>All of the parts are dishwasher safe apart from the motor base. Before I loaded it into the dishwasher, I rinsed off as much pulp as possible, but there was still some residue after the wash cycle. I went in with the provided cleaning brush to get the final bits out, but it took me about half an hour of scrubbing to be happy with the cleanliness of the machine. Tiny bits of pulp get stuck in the smallest crevices you’ve ever seen — nightmare.</p><h2 class="article-body__section" id="section-canoly-c16-cold-press-juicer-review-verdict"><span>Canoly C16 Cold Press Juicer review: Verdict</span></h2><p>The Canoly C16 Cold Press is a 3-in-1 machine, so I was able to make milk, sorbet and juice. It’s very useful that all of the necessary attachments are included when you purchase the juicer. It’s very quiet when operating too, so it doesn’t cause too much of a commotion in the home. </p><p>The large hopper makes it easy to juice in batches if you need to, and it couldn’t be easier to use, with just one switch that controls everything. </p><p>However, this juice wastes a lot of your time through cleaning and waiting for it to stop overheating. Cleaning is incredibly difficult, with pulp getting caught up in every crevice you can think of, the machine frequently clogs, meaning you have to be on hand to hit the reverse switch. And it overheats when juicing fruits that should be easy, like oranges. </p><p>If you seriously want to get into juicing, I would recommend spending more money on a Nama juicer like the <a href="https://www.tomsguide.com/home/nama-j3-cold-press-juicer-review">Nama J3</a>.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ People are seriously spending $300 on a handheld coffee grinder, and here's why I'm completely on their side ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the twenty-second episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>In the coffee sphere, a select few brands are so well-respected that they elicit a revered gasp when mentioned. Moccamaster is one, as is Lelit, Acaia, and La Marzocco. Of course, this isn't an exhaustive list, and you've also got the universally adored Mazzer and Comandante, which make the <a href="https://www.tomsguide.com/best-picks/best-coffee-grinder">best coffee grinders</a>.</p><p>Comandante is what I'm going to discuss today. You may have heard of the <a href="https://www.tomsguide.com/home/home-appliances/comandante-c40-mk4-review">Comandante C40 MK4</a>, the Comandante C60 Baracuda, or the Comandante X25 Trailmaster. The C60 Baracuda is the priciest option, at around $500-$650 depending on finish, and the X25 Trailmaster is the closest Comandante gets to "budget" — $250. But let's focus on the midrange option, the C40 MK4. </p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7680226127113243918" data-video-id="7680226127113243918" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ Heartwarming, everyday, funny BGM(1194980) - K's note" href="https://www.tiktok.com/music/Heartwarming-everyday-funny-BGM-1194980-7065431325666183169">♬ Heartwarming, everyday, funny BGM(1194980) - K's note</a></section>                    </blockquote></div>                <p>I've owned this coffee grinder for about 18 months now, and I'm seriously obsessed. I use it every day, multiple times a day, and it's simply perfection. While pricey, ranging from <a href="https://www.amazon.com/Comandante-4260113435663-Nitro-Blade-Black/dp/B07JQ4P976" target="_blank" rel="nofollow">$289-$325 at Amazon</a>, I'd seriously pay it for this performance. </p><div class="product"><a data-dimension112="21205f64-a2d3-11f1-aae5-a3cf0dca9955" data-action="Deal Block" data-label="C40 MK4" data-dimension48="C40 MK4" data-dimension25="$275" href="https://www.amazon.com/Comandante-4260113435663-Nitro-Blade-Black/dp/B07JQ4P976" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="CZ2VbSvqAbjXtZwv5d62A8" name="Comandante C40 MK4" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/CZ2VbSvqAbjXtZwv5d62A8.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>While pricey, the Comandante C40 MK4 earns its keep — the Martensitic steel burrs have a lifetime warranty and it's constructed from high-quality metals or wood (depending on your chosen finish). I have the pictured walnut version.<a class="view-deal button" href="https://www.amazon.com/Comandante-4260113435663-Nitro-Blade-Black/dp/B07JQ4P976" target="_blank" rel="nofollow" data-dimension112="21205f64-a2d3-11f1-aae5-a3cf0dca9955" data-action="Deal Block" data-label="C40 MK4" data-dimension48="C40 MK4" data-dimension25="$275">View Deal</a></p></div><h2 id="what-actually-is-the-comandante-c40-mk4">What actually is the Comandante C40 MK4? </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="wXomSExhKizcfgBFv5Jk4B" name="Comandante_C40_Mk4_grinder_ 4.JPG" alt="the comandante c40 mk4 hand coffee grinder with a glass catch chamber and high nitrogen steel burrs" src="https://cdn.mos.cms.futurecdn.net/wXomSExhKizcfgBFv5Jk4B.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Comandante C40 MK4 is a handheld coffee grinder. This means you have to turn the burrs yourself. The hand crank is easy to grip and it comes with a rubber band you can put around the canister to secure your hold. The rubber band is "supposed" to be a wristband but I use it for holding the grinder steady (I'm weak). </p><p>So while other coffee grinders are powered by an electric motor, like the <a href="https://www.tomsguide.com/home/home-appliances/baratza-encore-esp-review">Baratza Encore ESP</a> or the <a href="https://www.tomsguide.com/home/coffee-makers/mazzer-philos-review">Mazzer Philos</a>, the Comandante C40 MK4 is all you, baby. </p><p>Hand grinders are generally cheaper than electric grinders. I've tested some as cheap as $60 —like the <a href="https://www.tomsguide.com/home/kitchen-dining/timemore-chestnut-c2s-review">Timemore Chestnut C2s</a> — but the Comandante is by far the priciest (and best). Naturally, electric grinders can set you back much, much more than the C40 MK4. The Eureka Mignon Specialita is $699, and the Mazzer Philos is $1,500.</p><p>As a hand grinder, its maximum capacity is around 50g, but I would only recommend grinding enough for one or two espressos (so 36g) at once. When the bottom canister gets full, you can't grind any more. </p><h2 id="why-is-a-hand-grinder-worth-300">Why is a hand grinder worth $300? </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="sZoB6aKU56X5auqv5K2y4B" name="Comandante_C40_Mk4_grinder_.JPG" alt="the comandante c40 mk4 hand coffee grinder with a glass catch chamber and high nitrogen steel burrs" src="https://cdn.mos.cms.futurecdn.net/sZoB6aKU56X5auqv5K2y4B.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Comandante C40 MK4 is worth $300 because of one reason: materials. The grinder uses Martensitic steel burrs, which is the same material used in sharp surgical instruments. Oh, and the burrs also come with a lifetime warranty. No biggie. </p><p>Naturally, this quality material results in excellent uniformity, minimal retention, and low static. These are the three touchstones of a great grinder. You want all your coffee grounds to be the same size, you want the grinder to retain as little coffee as possible, and you don't want your worktop to be covered in sticky coffee granules post-grind. </p><p>Thankfully, the grinder excels in all three. Here's a little insight into the grinder's uniformity. Here, I'm checking if all the coffee grounds are the same size. You want big jumps: 0% 300um to 100% 500um, for example.</p><div ><table><thead><tr><th class="firstcol " ><p><strong>Grind size</strong></p></th><th  ><p><strong>300μm</strong></p></th><th  ><p><strong>500μm</strong></p></th><th  ><p><strong>800μm</strong></p></th><th  ><p><strong>1100μm</strong></p></th><th  ><p><strong>1400μm</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p>1 click (min/finest)</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>4 clicks</p></td><td  ><p>99%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>8 clicks</p></td><td  ><p>90%</p></td><td  ><p>99%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>12 clicks</p></td><td  ><p>1%</p></td><td  ><p>95%</p></td><td  ><p>99%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>20 clicks</p></td><td  ><p>1%</p></td><td  ><p>80%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>24 clicks</p></td><td  ><p>0%</p></td><td  ><p>20%</p></td><td  ><p>90%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>32 clicks</p></td><td  ><p>0%</p></td><td  ><p>30%</p></td><td  ><p>95%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>36 clicks</p></td><td  ><p>0%</p></td><td  ><p>0%</p></td><td  ><p>1%</p></td><td  ><p>30%</p></td><td  ><p>50%</p></td></tr><tr><td class="firstcol " ><p>40 clicks (max/coarsest)</p></td><td  ><p>0%</p></td><td  ><p>0%</p></td><td  ><p>0%</p></td><td  ><p>0%</p></td><td  ><p>10%</p></td></tr></tbody></table></div><p>As you can see, the consistency is excellent. Most granules are the same size, which will result in a better-tasting coffee. If you've got some grains at >300um, but some at 1400um>, this means the coffee will be half over- and half under-extracted. Ergo, no thank you, I'll just drink my water. </p><h2 id="should-i-spend-300-on-a-coffee-grinder">Should I spend $300 on a coffee grinder? </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="yczfdS2GVsvYrwKUgMU65B" name="Comandante_C40_Mk4_grinder_ 7.JPG" alt="the comandante c40 mk4 hand coffee grinder with a glass catch chamber and high nitrogen steel burrs" src="https://cdn.mos.cms.futurecdn.net/yczfdS2GVsvYrwKUgMU65B.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you have $300 to spare and you're into coffee, yes, you should buy this grinder. All that being said, though, if you don't have $300 lying around, then please don't buy an objectively expensive piece of coffee tech. You can still get a good coffee with a cheaper grinder, like the aforementioned Timemore Chestnut C2s. </p><p>I love my Comandante C40 MK4, but it's not for everyone. In this economy, I think there are more important things to invest in. Save your dollars for a rainy day! </p><p>But if you've got loads of cash spare, then of course you won't be disappointed by this incredible coffee grinder. It's my favorite grinder of all time... I just wish the military branding wasn't so cringe. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OaLgwW"></div>                            </div>                            <script src="https://kwizly.com/embed/OaLgwW.js" async></script><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/overwhelmed-by-coffee-jargon-here-are-the-5-key-espresso-terms-you-need-to-know"><strong>Overwhelmed by coffee jargon? Here are the 5 key espresso terms you need to know</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/im-an-ex-starbucks-barista-you-can-make-the-iced-brown-sugar-oatmilk-shaken-espresso-at-home-for-a-fraction-of-the-cost"><strong>I'm an ex-Starbucks barista — you can make the Iced Brown Sugar Oatmilk Shaken Espresso at home for a fraction of the cost</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/people-are-mixing-coffee-with-diet-coke-yes-really-im-an-ex-barista-and-i-tried-the-viral-fridge-cigarette"><strong>People are mixing coffee with Diet Coke (yes, really) — I'm an ex-barista, and I tried the viral 'fridge cigarette'</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/people-are-seriously-spending-usd300-on-a-handheld-coffee-grinder-and-heres-why-im-completely-on-their-side</link>
                                                                            <description>
                            <![CDATA[ On episode 22 of The Coffee Lab, I'm diving deep into the Comandante C40 MK4: Why does this pricey coffee grinder have a cult following? ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">H3B7qrJtKRFZBD9tsBKfvW</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/oWcc46QcegZzZbfbugKkWj-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 31 Aug 2026 11:30:00 +0000</pubDate>                                                                                                                                <updated>Mon, 31 Aug 2026 16:08:10 +0000</updated>
                                                                                                                                            <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/oWcc46QcegZzZbfbugKkWj-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the comandante c40 mk4 with the coffee lab logo]]></media:description>                                                            <media:text><![CDATA[the comandante c40 mk4 with the coffee lab logo]]></media:text>
                                <media:title type="plain"><![CDATA[the comandante c40 mk4 with the coffee lab logo]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/oWcc46QcegZzZbfbugKkWj-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the twenty-second episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>In the coffee sphere, a select few brands are so well-respected that they elicit a revered gasp when mentioned. Moccamaster is one, as is Lelit, Acaia, and La Marzocco. Of course, this isn't an exhaustive list, and you've also got the universally adored Mazzer and Comandante, which make the <a href="https://www.tomsguide.com/best-picks/best-coffee-grinder">best coffee grinders</a>.</p><p>Comandante is what I'm going to discuss today. You may have heard of the <a href="https://www.tomsguide.com/home/home-appliances/comandante-c40-mk4-review">Comandante C40 MK4</a>, the Comandante C60 Baracuda, or the Comandante X25 Trailmaster. The C60 Baracuda is the priciest option, at around $500-$650 depending on finish, and the X25 Trailmaster is the closest Comandante gets to "budget" — $250. But let's focus on the midrange option, the C40 MK4. </p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7680226127113243918" data-video-id="7680226127113243918" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ Heartwarming, everyday, funny BGM(1194980) - K's note" href="https://www.tiktok.com/music/Heartwarming-everyday-funny-BGM-1194980-7065431325666183169">♬ Heartwarming, everyday, funny BGM(1194980) - K's note</a></section>                    </blockquote></div>                <p>I've owned this coffee grinder for about 18 months now, and I'm seriously obsessed. I use it every day, multiple times a day, and it's simply perfection. While pricey, ranging from <a href="https://www.amazon.com/Comandante-4260113435663-Nitro-Blade-Black/dp/B07JQ4P976" target="_blank" rel="nofollow">$289-$325 at Amazon</a>, I'd seriously pay it for this performance. </p><div class="product"><a data-dimension112="21205f64-a2d3-11f1-aae5-a3cf0dca9955" data-action="Deal Block" data-label="C40 MK4" data-dimension48="C40 MK4" data-dimension25="$275" href="https://www.amazon.com/Comandante-4260113435663-Nitro-Blade-Black/dp/B07JQ4P976" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="CZ2VbSvqAbjXtZwv5d62A8" name="Comandante C40 MK4" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/CZ2VbSvqAbjXtZwv5d62A8.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>While pricey, the Comandante C40 MK4 earns its keep — the Martensitic steel burrs have a lifetime warranty and it's constructed from high-quality metals or wood (depending on your chosen finish). I have the pictured walnut version.<a class="view-deal button" href="https://www.amazon.com/Comandante-4260113435663-Nitro-Blade-Black/dp/B07JQ4P976" target="_blank" rel="nofollow" data-dimension112="21205f64-a2d3-11f1-aae5-a3cf0dca9955" data-action="Deal Block" data-label="C40 MK4" data-dimension48="C40 MK4" data-dimension25="$275">View Deal</a></p></div><h2 id="what-actually-is-the-comandante-c40-mk4">What actually is the Comandante C40 MK4? </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="wXomSExhKizcfgBFv5Jk4B" name="Comandante_C40_Mk4_grinder_ 4.JPG" alt="the comandante c40 mk4 hand coffee grinder with a glass catch chamber and high nitrogen steel burrs" src="https://cdn.mos.cms.futurecdn.net/wXomSExhKizcfgBFv5Jk4B.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Comandante C40 MK4 is a handheld coffee grinder. This means you have to turn the burrs yourself. The hand crank is easy to grip and it comes with a rubber band you can put around the canister to secure your hold. The rubber band is "supposed" to be a wristband but I use it for holding the grinder steady (I'm weak). </p><p>So while other coffee grinders are powered by an electric motor, like the <a href="https://www.tomsguide.com/home/home-appliances/baratza-encore-esp-review">Baratza Encore ESP</a> or the <a href="https://www.tomsguide.com/home/coffee-makers/mazzer-philos-review">Mazzer Philos</a>, the Comandante C40 MK4 is all you, baby. </p><p>Hand grinders are generally cheaper than electric grinders. I've tested some as cheap as $60 —like the <a href="https://www.tomsguide.com/home/kitchen-dining/timemore-chestnut-c2s-review">Timemore Chestnut C2s</a> — but the Comandante is by far the priciest (and best). Naturally, electric grinders can set you back much, much more than the C40 MK4. The Eureka Mignon Specialita is $699, and the Mazzer Philos is $1,500.</p><p>As a hand grinder, its maximum capacity is around 50g, but I would only recommend grinding enough for one or two espressos (so 36g) at once. When the bottom canister gets full, you can't grind any more. </p><h2 id="why-is-a-hand-grinder-worth-300">Why is a hand grinder worth $300? </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="sZoB6aKU56X5auqv5K2y4B" name="Comandante_C40_Mk4_grinder_.JPG" alt="the comandante c40 mk4 hand coffee grinder with a glass catch chamber and high nitrogen steel burrs" src="https://cdn.mos.cms.futurecdn.net/sZoB6aKU56X5auqv5K2y4B.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Comandante C40 MK4 is worth $300 because of one reason: materials. The grinder uses Martensitic steel burrs, which is the same material used in sharp surgical instruments. Oh, and the burrs also come with a lifetime warranty. No biggie. </p><p>Naturally, this quality material results in excellent uniformity, minimal retention, and low static. These are the three touchstones of a great grinder. You want all your coffee grounds to be the same size, you want the grinder to retain as little coffee as possible, and you don't want your worktop to be covered in sticky coffee granules post-grind. </p><p>Thankfully, the grinder excels in all three. Here's a little insight into the grinder's uniformity. Here, I'm checking if all the coffee grounds are the same size. You want big jumps: 0% 300um to 100% 500um, for example.</p><div ><table><thead><tr><th class="firstcol " ><p><strong>Grind size</strong></p></th><th  ><p><strong>300μm</strong></p></th><th  ><p><strong>500μm</strong></p></th><th  ><p><strong>800μm</strong></p></th><th  ><p><strong>1100μm</strong></p></th><th  ><p><strong>1400μm</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p>1 click (min/finest)</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>4 clicks</p></td><td  ><p>99%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>8 clicks</p></td><td  ><p>90%</p></td><td  ><p>99%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>12 clicks</p></td><td  ><p>1%</p></td><td  ><p>95%</p></td><td  ><p>99%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>20 clicks</p></td><td  ><p>1%</p></td><td  ><p>80%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>24 clicks</p></td><td  ><p>0%</p></td><td  ><p>20%</p></td><td  ><p>90%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>32 clicks</p></td><td  ><p>0%</p></td><td  ><p>30%</p></td><td  ><p>95%</p></td><td  ><p>100%</p></td><td  ><p>100%</p></td></tr><tr><td class="firstcol " ><p>36 clicks</p></td><td  ><p>0%</p></td><td  ><p>0%</p></td><td  ><p>1%</p></td><td  ><p>30%</p></td><td  ><p>50%</p></td></tr><tr><td class="firstcol " ><p>40 clicks (max/coarsest)</p></td><td  ><p>0%</p></td><td  ><p>0%</p></td><td  ><p>0%</p></td><td  ><p>0%</p></td><td  ><p>10%</p></td></tr></tbody></table></div><p>As you can see, the consistency is excellent. Most granules are the same size, which will result in a better-tasting coffee. If you've got some grains at >300um, but some at 1400um>, this means the coffee will be half over- and half under-extracted. Ergo, no thank you, I'll just drink my water. </p><h2 id="should-i-spend-300-on-a-coffee-grinder">Should I spend $300 on a coffee grinder? </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="yczfdS2GVsvYrwKUgMU65B" name="Comandante_C40_Mk4_grinder_ 7.JPG" alt="the comandante c40 mk4 hand coffee grinder with a glass catch chamber and high nitrogen steel burrs" src="https://cdn.mos.cms.futurecdn.net/yczfdS2GVsvYrwKUgMU65B.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you have $300 to spare and you're into coffee, yes, you should buy this grinder. All that being said, though, if you don't have $300 lying around, then please don't buy an objectively expensive piece of coffee tech. You can still get a good coffee with a cheaper grinder, like the aforementioned Timemore Chestnut C2s. </p><p>I love my Comandante C40 MK4, but it's not for everyone. In this economy, I think there are more important things to invest in. Save your dollars for a rainy day! </p><p>But if you've got loads of cash spare, then of course you won't be disappointed by this incredible coffee grinder. It's my favorite grinder of all time... I just wish the military branding wasn't so cringe. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OaLgwW"></div>                            </div>                            <script src="https://kwizly.com/embed/OaLgwW.js" async></script><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/overwhelmed-by-coffee-jargon-here-are-the-5-key-espresso-terms-you-need-to-know"><strong>Overwhelmed by coffee jargon? Here are the 5 key espresso terms you need to know</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/im-an-ex-starbucks-barista-you-can-make-the-iced-brown-sugar-oatmilk-shaken-espresso-at-home-for-a-fraction-of-the-cost"><strong>I'm an ex-Starbucks barista — you can make the Iced Brown Sugar Oatmilk Shaken Espresso at home for a fraction of the cost</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/people-are-mixing-coffee-with-diet-coke-yes-really-im-an-ex-barista-and-i-tried-the-viral-fridge-cigarette"><strong>People are mixing coffee with Diet Coke (yes, really) — I'm an ex-barista, and I tried the viral 'fridge cigarette'</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested Subminimal’s newest handheld milk frother — and it’s so much more than a coffee accessory ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Subminimal is one of the most “Oooh! Aaah!”-inducing names in the coffee world, and the new Subminimal Nanofoamer Go is no different. Ever looked at a milk frother and thought, “Damn, I wish that came with a carry case?” Well, your calls have been answered — the Nanofoamer Go comes with three impellers, ten color options, and, yes, a carry case. </p><p>If you’re the kind of coffee drinker who takes your whole setup to a campsite, Airbnb, or simply to the office, this will be ideal for you. While I struggled to achieve the same velvety microfoam I can make on the Subminimal Nanofoamer Pro, the Go can also whisk cream and eggs, so I guess it wins there. </p><p>So, is this the handheld milk frother for you? If you value affordability, fancy colors, and portability, I’m leaning towards yes. Are you a coffee snob? Hm… you might want to splurge on the $159 Nanofoamer Pro instead. To find out the full story, keep reading this Subminimal Nanofoamer Go review. </p><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-specs"><span>Subminimal Nanofoamer Go review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Subminimal-NanoFoamer-Rechargeable-Handheld-Cabernet/dp/B0GXMW21BP/" target="_blank" rel="nofollow">$34</a> / <a href="https://subminimal.com/products/nanofoamer-go" target="_blank" rel="nofollow">£34</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.68 oz</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>1 x 1 x 8.1 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Impellers</strong></p></td><td  ><p>3: whisk, mixer, microfoam mesh</p></td></tr><tr><td class="firstcol " ><p><strong>RPM</strong></p></td><td  ><p>5,300</p></td></tr><tr><td class="firstcol " ><p><strong>Power</strong></p></td><td  ><p>Rechargeable USB-C</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-price-availability"><span>Subminimal Nanofoamer Go review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nkCWVCgG4VFKVB8fYxstEF" name="Subminimal Nanofoamer Go_003.JPG" alt="the subminimal nanofoamer go in sky blue photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/nkCWVCgG4VFKVB8fYxstEF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Subminimal Nanofoamer Go is considerably cheaper than the brand’s other milk frothers — just <a href="https://subminimal.com/products/nanofoamer-go" target="_blank" rel="nofollow">$37 from Subminimal U.S.</a> and <a href="https://subminimal.com/products/nanofoamer-go" target="_blank" rel="nofollow">£34 from Subminimal U.K.</a> On <a href="https://www.amazon.com/Subminimal-NanoFoamer-Rechargeable-Handheld-Cabernet/dp/B0GXMW21BP/" target="_blank" rel="nofollow">Amazon, it's $34</a>.</p><p>Subminimal’s Nanofoamer Lithium is $57, while the Subminimal Nanofoamer Pro (my personal milk frother) is $159. While both of these models are better at making straight-up cold foam and microfoam, if you prioritize budget and portability over pure functionality, you’d likely be better off with the Nanofoamer Go. Personally, I prefer the Nanofoamer Pro — it’s much better at making honest-to-God microfoam than the Go, as I’ll get into later.</p><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-design"><span>Subminimal Nanofoamer Go review: Design </span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kXVr7XRmpeEGveJmNrqXGE" name="Subminimal Nanofoamer Go_006.JPG" alt="the subminimal nanofoamer go in sky blue photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/kXVr7XRmpeEGveJmNrqXGE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As with everything else made by Subminimal, the Nanofoamer Go looks and feels awesome. Alright, the body is quite plasticky, but I don’t mind because it means it’s relatively lightweight and affordable. </p><p>But the best part? The colors. While the Subminimal Nanofoamer Lithium and Pro are both available solely in minimalist black, the Go comes in a huge ten colors. And every single one of them looks gorgeous. As you can see from the images in this review, I tested the sky blue version. Had I chosen the color for myself, I would have picked the terracotta or sage shades. Both are pretty enough to win a beauty pageant. </p><p>You also get a carry case! The clue’s in the name, but the Nanofoamer Go is intended to be portable. You’re supposed to take it on trips with you. AeroPress with foamed milk at the campsite? Yes, please. As expected, the carry case is the same lovely blue shade, and all the attachments click nicely into place. It’s satisfying to hold and use. I have nothing bad to say about the design here. </p><p>Of course, you get three impeller attachments. One is a whisk for thicker ingredients like cream and eggs. One is for mix-ins, like hot chocolate powder, matcha, so on and so forth, and the third is Subminimal’s patent-pending nanofoam mesh. Now, this is supposed to be the “secret” to barista-quality microfoam… but more on that later.</p><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-performance"><span>Subminimal Nanofoamer Go review: Performance</span></h2><p>I’m just going to rip the Band-Aid off: I didn’t get microfoam with the Nanofoamer Go at all. I tried with dairy and non-dairy milk, heated to varying temperatures, frothed in various ways. I tried on full power, low power; I tried for ten seconds, thirty seconds, two minutes, five minutes, one minute, everything. Did I achieve microfoam? No. No, I did not. Here’s a photo of the closest I got. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4032px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gYVUX7NjZChwMnj2e5Ns9S" name="subminimal microfoam" alt="a milk drink made by the subminimal nanofoamer go" src="https://cdn.mos.cms.futurecdn.net/gYVUX7NjZChwMnj2e5Ns9S.jpg" mos="" align="middle" fullscreen="" width="4032" height="2268" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, there are some relatively large bubbles. This is after banging and swirling the milk as you’re supposed to. I was quite disappointed by this. As mentioned earlier, my Subminimal Nanofoamer Pro <em>can</em> make microfoam, so I expected the same of the Go.</p><p>I also struggled to achieve velvety cold foam. I frothed this milk for over two minutes and this is the thickest it got. Of course, I was using the microfoam attachment. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3463px;"><p class="vanilla-image-block" style="padding-top:56.22%;"><img id="dyudfhzQX99YGRKGewCMxR" name="subminimal cold foam" alt="a milk drink made by the subminimal nanofoamer go" src="https://cdn.mos.cms.futurecdn.net/dyudfhzQX99YGRKGewCMxR.jpg" mos="" align="middle" fullscreen="" width="3463" height="1947" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>However, I was able to get delicious, perfect sweet cream cold foam in just forty seconds. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4933px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LEo7BjYAhUxA7bqmhhrKdS" name="subminimal whip topping" alt="a milk drink made by the subminimal nanofoamer go" src="https://cdn.mos.cms.futurecdn.net/LEo7BjYAhUxA7bqmhhrKdS.jpg" mos="" align="middle" fullscreen="" width="4933" height="2775" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the texture is thick, fluffy, and creamy. This is perfect, Starbucks-approved sweet cream cold foam. My colleagues and I literally ate this with a spoon. </p><p>According to Subminimal’s website, you can actually whisk eggs with the Nanofoamer Go… so of course I had to try it out. For science. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4459px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="QUZF5J5VeaBsXfrcMzcDYS" name="subminimal egg" alt="a milk drink made by the subminimal nanofoamer go" src="https://cdn.mos.cms.futurecdn.net/QUZF5J5VeaBsXfrcMzcDYS.jpg" mos="" align="middle" fullscreen="" width="4459" height="2509" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>Yes, that’s an egg whisked by a milk frother. I don’t eat eggs, so this is a little gross to me, but great if you’re a regular egg whipper. </p><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-storage-maintenance"><span>Subminimal Nanofoamer Go review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="SbD356xxGfjtLLyxaDVLFF" name="Subminimal Nanofoamer Go_009.JPG" alt="the subminimal nanofoamer go in sky blue photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/SbD356xxGfjtLLyxaDVLFF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As it’s a tiny milk frother — 1 inch by 8 inches — you’ll have no trouble fitting it in your cutlery drawer. This model comes with a handy carry case, which measures 2.1 x 6 x 1.3 inches, perfect for throwing in a camping backpack or keeping in a cupboard. If you’re short on space, I’d go for this model over the Nanofoamer Pro, which is shaped like a jug. </p><p>The Go is <em>not</em> dishwasher safe, and you obviously shouldn’t get the body wet. I cleaned it by simply rinsing it under warm water. </p><p>Of course, should your cleaning be sub-minimal (see what I did there? No?), you can buy <a href="https://subminimal.com/products/nanofoamer-go-nanoscreen-kit" target="_blank" rel="nofollow">replacement impellers directly from Subminimal for $6</a>.  </p><p>In terms of longevity, Subminimal offers a standard warranty of one year, which is a little shorter than I’d like. However, this is pretty standard with other $10-$20 milk frothers on Amazon.</p><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-verdict"><span>Subminimal Nanofoamer Go review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Z3oqoZaSEoZS9zNGaUB8EF" name="Subminimal Nanofoamer Go_002.JPG" alt="the subminimal nanofoamer go in sky blue photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Z3oqoZaSEoZS9zNGaUB8EF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Nanofoamer Go is more than just a milk frother — it can be a pretty little kitchen assistant, too. While this makes it more versatile than the Subminimal Nanofoamer Pro, it’s also not as coffee-snob-friendly. Even though it uses the same micro-mesh as other Nanofoamer products, it doesn’t seem to be able to achieve the same barista-smooth microfoam. </p><p>For $37, this is a neat little frother. I’d recommend it for casual coffee drinkers rather than those practising for the World Barista Championships. I would personally save up for the Subminimal Nanofoamer Pro or Lithium over the Go, but that’s because I take my cold foam seriously — and the Pro is much better at making it. </p><p>For generic froth, though, the Nanofoamer Go is great. Obviously, you have to heat your milk separately — microwave, stovetop — but if you want a one-stop for whisking eggs, cream cold foam, and milk, I’d go for the Go. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/subminimal-nanofoamer-go-review</link>
                                                                            <description>
                            <![CDATA[ The Subminimal Nanofoamer Go isn’t quite barista-worthy yet, but it’s a veritable sous chef in the kitchen department. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">B9JBG5bbGo6ADKMqRnE7fU</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/EcXasfu4pR4YCJkLv9ChDF-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 31 Aug 2026 07:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/EcXasfu4pR4YCJkLv9ChDF-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the subminimal nanofoamer go in sky blue photographed against the blue tom&#039;s guide background]]></media:description>                                                            <media:text><![CDATA[the subminimal nanofoamer go in sky blue photographed against the blue tom&#039;s guide background]]></media:text>
                                <media:title type="plain"><![CDATA[the subminimal nanofoamer go in sky blue photographed against the blue tom&#039;s guide background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/EcXasfu4pR4YCJkLv9ChDF-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Subminimal is one of the most “Oooh! Aaah!”-inducing names in the coffee world, and the new Subminimal Nanofoamer Go is no different. Ever looked at a milk frother and thought, “Damn, I wish that came with a carry case?” Well, your calls have been answered — the Nanofoamer Go comes with three impellers, ten color options, and, yes, a carry case. </p><p>If you’re the kind of coffee drinker who takes your whole setup to a campsite, Airbnb, or simply to the office, this will be ideal for you. While I struggled to achieve the same velvety microfoam I can make on the Subminimal Nanofoamer Pro, the Go can also whisk cream and eggs, so I guess it wins there. </p><p>So, is this the handheld milk frother for you? If you value affordability, fancy colors, and portability, I’m leaning towards yes. Are you a coffee snob? Hm… you might want to splurge on the $159 Nanofoamer Pro instead. To find out the full story, keep reading this Subminimal Nanofoamer Go review. </p><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-specs"><span>Subminimal Nanofoamer Go review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Subminimal-NanoFoamer-Rechargeable-Handheld-Cabernet/dp/B0GXMW21BP/" target="_blank" rel="nofollow">$34</a> / <a href="https://subminimal.com/products/nanofoamer-go" target="_blank" rel="nofollow">£34</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.68 oz</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>1 x 1 x 8.1 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Impellers</strong></p></td><td  ><p>3: whisk, mixer, microfoam mesh</p></td></tr><tr><td class="firstcol " ><p><strong>RPM</strong></p></td><td  ><p>5,300</p></td></tr><tr><td class="firstcol " ><p><strong>Power</strong></p></td><td  ><p>Rechargeable USB-C</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-price-availability"><span>Subminimal Nanofoamer Go review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nkCWVCgG4VFKVB8fYxstEF" name="Subminimal Nanofoamer Go_003.JPG" alt="the subminimal nanofoamer go in sky blue photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/nkCWVCgG4VFKVB8fYxstEF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Subminimal Nanofoamer Go is considerably cheaper than the brand’s other milk frothers — just <a href="https://subminimal.com/products/nanofoamer-go" target="_blank" rel="nofollow">$37 from Subminimal U.S.</a> and <a href="https://subminimal.com/products/nanofoamer-go" target="_blank" rel="nofollow">£34 from Subminimal U.K.</a> On <a href="https://www.amazon.com/Subminimal-NanoFoamer-Rechargeable-Handheld-Cabernet/dp/B0GXMW21BP/" target="_blank" rel="nofollow">Amazon, it's $34</a>.</p><p>Subminimal’s Nanofoamer Lithium is $57, while the Subminimal Nanofoamer Pro (my personal milk frother) is $159. While both of these models are better at making straight-up cold foam and microfoam, if you prioritize budget and portability over pure functionality, you’d likely be better off with the Nanofoamer Go. Personally, I prefer the Nanofoamer Pro — it’s much better at making honest-to-God microfoam than the Go, as I’ll get into later.</p><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-design"><span>Subminimal Nanofoamer Go review: Design </span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kXVr7XRmpeEGveJmNrqXGE" name="Subminimal Nanofoamer Go_006.JPG" alt="the subminimal nanofoamer go in sky blue photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/kXVr7XRmpeEGveJmNrqXGE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As with everything else made by Subminimal, the Nanofoamer Go looks and feels awesome. Alright, the body is quite plasticky, but I don’t mind because it means it’s relatively lightweight and affordable. </p><p>But the best part? The colors. While the Subminimal Nanofoamer Lithium and Pro are both available solely in minimalist black, the Go comes in a huge ten colors. And every single one of them looks gorgeous. As you can see from the images in this review, I tested the sky blue version. Had I chosen the color for myself, I would have picked the terracotta or sage shades. Both are pretty enough to win a beauty pageant. </p><p>You also get a carry case! The clue’s in the name, but the Nanofoamer Go is intended to be portable. You’re supposed to take it on trips with you. AeroPress with foamed milk at the campsite? Yes, please. As expected, the carry case is the same lovely blue shade, and all the attachments click nicely into place. It’s satisfying to hold and use. I have nothing bad to say about the design here. </p><p>Of course, you get three impeller attachments. One is a whisk for thicker ingredients like cream and eggs. One is for mix-ins, like hot chocolate powder, matcha, so on and so forth, and the third is Subminimal’s patent-pending nanofoam mesh. Now, this is supposed to be the “secret” to barista-quality microfoam… but more on that later.</p><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-performance"><span>Subminimal Nanofoamer Go review: Performance</span></h2><p>I’m just going to rip the Band-Aid off: I didn’t get microfoam with the Nanofoamer Go at all. I tried with dairy and non-dairy milk, heated to varying temperatures, frothed in various ways. I tried on full power, low power; I tried for ten seconds, thirty seconds, two minutes, five minutes, one minute, everything. Did I achieve microfoam? No. No, I did not. Here’s a photo of the closest I got. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4032px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gYVUX7NjZChwMnj2e5Ns9S" name="subminimal microfoam" alt="a milk drink made by the subminimal nanofoamer go" src="https://cdn.mos.cms.futurecdn.net/gYVUX7NjZChwMnj2e5Ns9S.jpg" mos="" align="middle" fullscreen="" width="4032" height="2268" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, there are some relatively large bubbles. This is after banging and swirling the milk as you’re supposed to. I was quite disappointed by this. As mentioned earlier, my Subminimal Nanofoamer Pro <em>can</em> make microfoam, so I expected the same of the Go.</p><p>I also struggled to achieve velvety cold foam. I frothed this milk for over two minutes and this is the thickest it got. Of course, I was using the microfoam attachment. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3463px;"><p class="vanilla-image-block" style="padding-top:56.22%;"><img id="dyudfhzQX99YGRKGewCMxR" name="subminimal cold foam" alt="a milk drink made by the subminimal nanofoamer go" src="https://cdn.mos.cms.futurecdn.net/dyudfhzQX99YGRKGewCMxR.jpg" mos="" align="middle" fullscreen="" width="3463" height="1947" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>However, I was able to get delicious, perfect sweet cream cold foam in just forty seconds. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4933px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LEo7BjYAhUxA7bqmhhrKdS" name="subminimal whip topping" alt="a milk drink made by the subminimal nanofoamer go" src="https://cdn.mos.cms.futurecdn.net/LEo7BjYAhUxA7bqmhhrKdS.jpg" mos="" align="middle" fullscreen="" width="4933" height="2775" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the texture is thick, fluffy, and creamy. This is perfect, Starbucks-approved sweet cream cold foam. My colleagues and I literally ate this with a spoon. </p><p>According to Subminimal’s website, you can actually whisk eggs with the Nanofoamer Go… so of course I had to try it out. For science. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4459px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="QUZF5J5VeaBsXfrcMzcDYS" name="subminimal egg" alt="a milk drink made by the subminimal nanofoamer go" src="https://cdn.mos.cms.futurecdn.net/QUZF5J5VeaBsXfrcMzcDYS.jpg" mos="" align="middle" fullscreen="" width="4459" height="2509" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>Yes, that’s an egg whisked by a milk frother. I don’t eat eggs, so this is a little gross to me, but great if you’re a regular egg whipper. </p><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-storage-maintenance"><span>Subminimal Nanofoamer Go review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="SbD356xxGfjtLLyxaDVLFF" name="Subminimal Nanofoamer Go_009.JPG" alt="the subminimal nanofoamer go in sky blue photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/SbD356xxGfjtLLyxaDVLFF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As it’s a tiny milk frother — 1 inch by 8 inches — you’ll have no trouble fitting it in your cutlery drawer. This model comes with a handy carry case, which measures 2.1 x 6 x 1.3 inches, perfect for throwing in a camping backpack or keeping in a cupboard. If you’re short on space, I’d go for this model over the Nanofoamer Pro, which is shaped like a jug. </p><p>The Go is <em>not</em> dishwasher safe, and you obviously shouldn’t get the body wet. I cleaned it by simply rinsing it under warm water. </p><p>Of course, should your cleaning be sub-minimal (see what I did there? No?), you can buy <a href="https://subminimal.com/products/nanofoamer-go-nanoscreen-kit" target="_blank" rel="nofollow">replacement impellers directly from Subminimal for $6</a>.  </p><p>In terms of longevity, Subminimal offers a standard warranty of one year, which is a little shorter than I’d like. However, this is pretty standard with other $10-$20 milk frothers on Amazon.</p><h2 class="article-body__section" id="section-subminimal-nanofoamer-go-review-verdict"><span>Subminimal Nanofoamer Go review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Z3oqoZaSEoZS9zNGaUB8EF" name="Subminimal Nanofoamer Go_002.JPG" alt="the subminimal nanofoamer go in sky blue photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Z3oqoZaSEoZS9zNGaUB8EF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Nanofoamer Go is more than just a milk frother — it can be a pretty little kitchen assistant, too. While this makes it more versatile than the Subminimal Nanofoamer Pro, it’s also not as coffee-snob-friendly. Even though it uses the same micro-mesh as other Nanofoamer products, it doesn’t seem to be able to achieve the same barista-smooth microfoam. </p><p>For $37, this is a neat little frother. I’d recommend it for casual coffee drinkers rather than those practising for the World Barista Championships. I would personally save up for the Subminimal Nanofoamer Pro or Lithium over the Go, but that’s because I take my cold foam seriously — and the Pro is much better at making it. </p><p>For generic froth, though, the Nanofoamer Go is great. Obviously, you have to heat your milk separately — microwave, stovetop — but if you want a one-stop for whisking eggs, cream cold foam, and milk, I’d go for the Go. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Yes, chef — shop these 21 hot kitchen appliances and cookware items for your foodie dad this Father's Day ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Only seven days left before <a href="https://www.tomsguide.com/sales-events/fathers-day-gift-ideas-au">Father's Day 2026</a>, which falls on Sunday, September 6 in Australia.</p><p>If dad enjoys cooking up a feast, or maybe hosting the occasional dinner party, there's no better way to celebrate him than nabbing a kitchen-inspired gift this year. From small appliances to cookware and drinkware, there's plenty to consider here, as I've spotted 21 of the best kitchen and dining Father's Day gifts below. </p><p>All of these items have 4-star reviews on <a href="https://www.amazon.com.au/b/?_encoding=UTF8&node=222081923051&pd_rd_w=FBhNx&content-id=amzn1.sym.c75f719b-9ab0-435d-b82c-dec893d0af07&pf_rd_p=c75f719b-9ab0-435d-b82c-dec893d0af07&pf_rd_r=PFYM99CHZD98Q1J4GKH2&pd_rd_wg=EuT2g&pd_rd_r=f88e13df-cee3-4e46-a377-4ee7cc0d9fc6&ref_=pd_hp_d_r_stg_unk" target="_blank">Amazon</a>, <a href="https://kitchenaid.com.au/collections/sale" target="_blank">KitchenAid</a>, and <a href="https://www.davidjones.com/gifts/fathers-day" target="_blank">David Jones</a>, plus they offer same-day or fast delivery, so your preferred gift will arrive just in time for the big day. </p><p>I'll stop with the fuss about now — here are the best kitchen-inspired Father's Day gifts available right now. </p>        <div class="featured_product_block featured_block_hero" data-id="c1a88b3a-a294-11f1-ad60-0b5c83491949">            <a href="https://www.amazon.com.au/Breville-Toast-4-Slice-Sandwich-Press/dp/B0DB4FBBN8" data-model-name="the Toast and Melt 4-Slice Sandwich Press, Black Truffle" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:150%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/K6fcFkU8ABMKyZMqoGW3UF.jpg" alt="Breville the Toast and Melt 4-Slice Sandwich Press, Black Truffle, Lsg545btr"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Breville</div>                                        <div class="featured__title">the Toast and Melt 4-Slice Sandwich Press, Black Truffle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88bc6-a294-11f1-ae98-c520c23c781d">            <a href="https://www.amazon.com.au/Ninja-CleanCrispTM-Functionality-Leak-Proof-FN101SGANZ" data-model-name="Crispi Air fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ji967wLx3MW4p5HDTHCYr.jpg" alt="Ninja Crispi 4-In-1 Glass Air Fryer | With Non-Toxic 4qt & 6-Cup Glass Containers & Lids | Microwave, Freezer, Dishwasher Safe | 450°f Max Temp |1500 Watt | Green | Fn101sg"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ninja</div>                                        <div class="featured__title">Crispi Air fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88c48-a294-11f1-87e1-937bd3e660f0">            <a href="https://www.amazon.com.au/DeLonghi-Nespresso-Single-Serve-Centrifusion-Technology/dp/B0BSV6MRM6" data-model-name="Vertuo Pop, Pacific Blue" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/AXJpc6UQSuFnqkTzxfZxME.jpg" alt="De'longhi Nespresso Vertuo Pop Env90.a, Capsule Coffee Machine, Single-Serve Pod Coffee Machine, 4 Cup Sizes, Nespresso Starter Pack, Centrifusion Technology, Smart Connectivity, Pacific Blue"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nespresso</div>                                        <div class="featured__title">Vertuo Pop, Pacific Blue</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88cc0-a294-11f1-8678-0fd4b4f2a7aa">            <a href="https://www.amazon.com.au/Russell-Hobbs-21670-70-red-21670-70-Stainless/dp/B01J1RWURS" data-model-name="Kettle 1.7L" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/DYC8nLQgckrFk6ZqZreo4F.jpg" alt="Russell Hobbs Kettle [1.7 L, 2400 W] Retro Red and Polished Stainless Steel (quick Boil Function, Water Temperature Display in Retro Design, External Water Level Indicator) Vintage Tea Maker 21670-70"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Russell Hobbs</div>                                        <div class="featured__title">Kettle 1.7L</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88d42-a294-11f1-8b98-fde935f6a04c">            <a href="https://www.amazon.com.au/dreame-Portable-Blender-BPA-Free-Self-Cleaning/dp/B0H1C1FWS1" data-model-name="Portable Blender" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jLMNZRNpYRLB3GiFgnzsBE.jpg" alt="Dreame Portable Blender, 880 Ml Bpa-Free Cup, 5 Presets Self-Cleaning Green"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Dreame</div>                                        <div class="featured__title">Portable Blender</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88dba-a294-11f1-8111-27021b787040">            <a href="https://kitchenaid.com.au/products/kitchenaid-3-5-cup-mini-food-chopper-kfc3516a-1" data-model-name="3.5 Cup Mini Food Chopper" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:70.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/hFuK2rRP3Y3Ek6BVK3YqMF.jpg" alt="3.5 Cup Mini Food Chopper Kfc3516a"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KitchenAid</div>                                        <div class="featured__title">3.5 Cup Mini Food Chopper</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88e32-a294-11f1-a8af-8379444a3035">            <a href="https://www.amazon.com.au/CERA-Self-Heating-Portable-Espresso-CERApresso/dp/B0FHPS5LXC/ref=sr_1_14" data-model-name="Self Heating Portable Espresso Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/mQsroLpQbkgbC3ZVaNnyEE.jpg" alt="Cera+ Self Heating Portable Espresso Machine, 20 Bar Coffee Maker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>CERA+</div>                                        <div class="featured__title">Self Heating Portable Espresso Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88eaa-a294-11f1-b705-2d95cbfa0d87">            <a href="https://www.amazon.com.au/Bialetti-Express-Espresso-Maker-06799/dp/B0000AN3QI" data-model-name="Moka Express Stovetop Espresso Maker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/pFjtYdEq7g76bnKM5hL4xD.jpg" alt="Bialetti - Moka Express Stovetop Espresso Maker, 3 Cups (130 Ml) Capacity – Makes Real Italian Coffee – Iconic Moka Pot With Patented Safety Valve, Ergonomic Handle, Aluminium, Silver"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Bialetti</div>                                        <div class="featured__title">Moka Express Stovetop Espresso Maker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88f2c-a294-11f1-9343-c196cabfbec0">            <a href="https://www.amazon.com.au/Wirefree-Thermometer-INT-11I-B-Bluetooth-Waterproof/dp/B0GSQ6JPN7" data-model-name="Mini Wirefree Meat BBQ Thermometer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/FhqiRzkT8m3hz3D37XkvYF.jpg" alt="Inkbird Mini Wirefree Meat Bbq Thermometer Int-11i-B, Bluetooth 305m Wide Distance, 1s Fast Readout,built-In 500mah Lithium Battery Ip67 Waterproof 25 Min Fast Charge for Kitchen Bbq Grill Oven Smoker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Inkbird</div>                                        <div class="featured__title">Mini Wirefree Meat BBQ Thermometer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88fa4-a294-11f1-88d2-1fc9e87d2cfd">            <a href="https://www.amazon.com.au/Russell-Hobbs-Slow-Cooker-Black/dp/B07D8RNVP1" data-model-name="7L Slow Cooker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/YmDaRNLqgrZpbrjVETCi4E.jpg" alt="Russell Hobbs 7l Slow Cooker, Rhsc7, 3 Heat Settings (low, High and Keep Warm), Dishwasher-Safe Ceramic Pot, Built-In Lid Holder, Cooks 8 Servings - Matte Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Russell Hobbs</div>                                        <div class="featured__title">7L Slow Cooker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a8901c-a294-11f1-b893-77ad33e72ad8">            <a href="https://www.amazon.com.au/Scanpan-Impact-Frypan-20cm-28cm/dp/B077P2X216" data-model-name="Impact Stainless Steel Fry Pan Set 2pk" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:67.80%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/NL6AZoyN5KcXPvJHF5dY7E.jpg" alt="Scanpan Impact 2-Piece Stainless Steel Fry Pan Set, 20 Cm & 28 Cm"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Scanpan</div>                                        <div class="featured__title">Impact Stainless Steel Fry Pan Set 2pk</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a89094-a294-11f1-b0f7-917703e79ee4">            <a href="https://www.amazon.com.au/Tefal-Non-Stick-C2893802-Aluminium-Technology/dp/B0CKJ98P83" data-model-name="Daily Expert Red Non-Stick Pancake Pan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/UYrtuasmjhDnXm9ijbYy7F.jpg" alt="Tefal Daily Expert Red Non-Stick Pancake Pan, 25cm, C2893802, Fixed Handle Aluminium, Titanium Non-Stick Coating, Thermo Signal Technology for Easier Cooking, Suitable for All Cooktops, Oven Safe"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Tefal</div>                                        <div class="featured__title">Daily Expert Red Non-Stick Pancake Pan</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a8910c-a294-11f1-960a-85c111315b52">            <a href="https://www.amazon.com.au/Tefal-H912S217-Induction-Non-Stick-Anodised/dp/B08F5FHFHZ/" data-model-name="Jamie Oliver by Tefal Cooks Classic Non-Stick Frypan 2pk" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/vMoYTpu9ZP3QZZQ6FuDTJE.jpg" alt="Tefal Jamie Oliver by Tefal Cooks Classic Non-Stick Induction Hard Anodised Twinpack Frypan 24/28cm, H912s217"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Tefal</div>                                        <div class="featured__title">Jamie Oliver by Tefal Cooks Classic Non-Stick Frypan 2pk</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a8918e-a294-11f1-8f94-8166f1c5d089">            <a href="https://www.amazon.com.au/Corelle-Livingware-18-Piece-Dinnerware-1088609/dp/B00F39GFUW/ref=sr_1_1" data-model-name="Winter Frost White Dinnerware Set, 18-Piece" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/xmi2LzHp76FSPcQk4oh3BF.jpg" alt="Corelle Winter Frost White Dinnerware Set, 18-Piece – Chip-Resistant Triple-Layer Glass – Stackable Design for Everyday Dining, 6 Dinner Plates, 6 Bread & Butter Plates, 4 Soup/cereal Bowls"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Corelle</div>                                        <div class="featured__title">Winter Frost White Dinnerware Set, 18-Piece</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a89206-a294-11f1-8352-b516830da536">            <a href="https://www.amazon.com.au/Multi-Prep-Compact-4pc-Prep-Mcolor/dp/B0CF6B979W/" data-model-name="Multi-Prep Compact 4-In-1 Multifunctional Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:81.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/aH9quCbNdCBLushj2sREWE.jpg" alt="Joseph Joseph Multi-Prep Compact 4-In-1 Multifunctional Set, Kitchen Vegetable Chopper & Dicer, Cheese Grater, Mandoline Veggie Slicer, Food Julienne Cutter With Container, Stainless Steel Blades"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Multi-Prep Compact 4-In-1 Multifunctional Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a8927e-a294-11f1-b0e6-e53adc173fd2">            <a href="https://www.amazon.com.au/Joseph-Colander-Strainer-Vegetable-Space-Saving/dp/B0DPG6JG3K" data-model-name="Droplet Mini Colander With Folding Handles" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:81.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/CeYv85aBMMtAFpuDZSCzdF.jpg" alt="Joseph Joseph Droplet Mini Colander With Folding Handles, Kitchen Strainer for Small Vegetable, Fruits & Berries, Space-Saving"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Droplet Mini Colander With Folding Handles</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a89300-a294-11f1-96c6-9b95201bdf3c">            <a href="https://www.amazon.com.au/Whiskey-Smoker-Kit-Torch-Fashioned/dp/B0BJV68C17" data-model-name="Whiskey Smoker Kit With Torch" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rTmtej8Z2JJXuFp3sXAWSE.jpg" alt="Whiskey Smoker Kit With Torch - 6 Flavors Wood Chips, 2 Glasses, 2 Ice Ball Molds - Cocktail Smoker Infuser Kit, Old Fashioned Drink Smoker Kit, Birthday Bourbon Whiskey Gifts for Men,dad(no Butane)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>ComboJoy</div>                                        <div class="featured__title">Whiskey Smoker Kit With Torch</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a89378-a294-11f1-93b1-cbe5dc88828b">            <a href="https://www.amazon.com.au/JoyJolt-Decanter-Lead-free-Crystal-Carafe/dp/B073NXQWBV/" data-model-name="Cigno Wine Decanter" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/qNmdy9ESusQDw9wfdYBe2F.jpg" alt="Joyjolt Cigno Wine Decanter Hand Blown Lead-Free Crystal Glass Red Wine Carafe 50.72 Fl. Oz."></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>JoyJolt</div>                                        <div class="featured__title">Cigno Wine Decanter</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a893f0-a294-11f1-952c-99681b5b87c9">            <a href="https://www.davidjones.com/product/gentlemens-hardware-top-hat-bottle-stoppers-set-of-2-28768167" data-model-name="Top Hat Bottle Stoppers - Set of 2" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:128.79%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/fXbMJwjuvpEfhYjyHxLhfU.png" alt="Top hat bottle tops"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Gentleman's Hardware</div>                                        <div class="featured__title">Top Hat Bottle Stoppers - Set of 2</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a89468-a294-11f1-b9db-b9707d1bf769">            <a href="https://www.davidjones.com/product/gentlemens-hardware-golf-ball-bottle-opener-28768171" data-model-name="Golf Ball Bottle Opener" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:128.79%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/AdgBT6PdKyFY9GZQFH82zP.png" alt="Gentleman's Hardware golf ball"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Gentleman's Hardware</div>                                        <div class="featured__title">Golf Ball Bottle Opener</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a894ea-a294-11f1-934b-31590118f811">            <a href="https://www.davidjones.com/product/gentlemens-hardware-fish-hip-flask-26067577" data-model-name="Fish Hip Flask" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:130.18%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/9gTVk8dqEaztEfXZzonhE7.png" alt="Gentleman's hardware fish flask"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Gentleman's Hardware</div>                                        <div class="featured__title">Fish Hip Flask</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/wellness/forget-socks-shop-these-30-expert-picked-skincare-and-haircare-buys-for-dad-this-fathers-day">Forget socks — shop these 30 expert-picked skincare and haircare buys for dad this Father's Day</a></li><li><a href="https://www.tomsguide.com/tech/forget-a-new-tie-ive-found-15-of-the-best-fathers-day-tech-gifts-that-will-make-you-his-favourite-child">Forget a new tie — I've found 15 of the best Father's Day tech gifts that will make you his favourite child</a></li><li><a href="https://www.tomsguide.com/gaming/gaming-peripherals/gamer-dad-ive-found-11-winning-fathers-day-gaming-ideas-to-level-up-your-gifting">Gamer dad? I've found 11 winning Father's Day gaming ideas to level up your gifting</a></li><li><a href="https://www.tomsguide.com/wellness/fitness/run-dont-walk-ive-spotted-9-of-the-best-fitness-fathers-day-gifts-thatll-help-dad-work-on-his-dadbod">Run, don't walk — I've spotted 9 of the best fitness Father's Day gifts that'll help dad work on his #DadBod</a></li><li><a href="https://www.tomsguide.com/home/gardening/got-an-outdoorsy-or-garden-loving-dad-ive-compiled-12-of-the-best-fathers-day-gifts-that-your-green-thumbed-dad-would-enjoy">Got an outdoorsy or garden-loving dad? I've compiled 12 of the best Father's Day gifts that your green-thumbed dad would enjoy</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/yes-chef-shop-these-21-hot-kitchen-appliances-and-cookware-items-for-your-foodie-dad-this-fathers-day</link>
                                                                            <description>
                            <![CDATA[ Give your head chef-dad one of these great kitchen-related gifts this Father's Day. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">fc4r3Z8GCHnReox2h2nCUc</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/KexY8jZGHNutJaacVxKDe4-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 29 Aug 2026 23:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ lucy.scotting@futurenet.com (Lucy Scotting) ]]></author>                    <dc:creator><![CDATA[ Lucy Scotting ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/UC9ncAYxkmJ5ipHEyX44ri.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Lucy Scotting is a staff writer for Tom’s Guide Australia, primarily covering lifestyle, streaming and internet-related news. Lucy started her career writing for HR and staffing industry publications, with articles covering emerging tech, business and finance.&lt;/p&gt;&lt;p&gt;In her spare time, Lucy can be found watching sci-fi movies, working on her dystopian fiction novel (in progress since 2017) or hanging out with her dog, Fletcher. If she’s not found doing any of the above, she’s likely on her next adventure to a new city, country, or continent. &lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/KexY8jZGHNutJaacVxKDe4-1280-80.jpg">
                                                            <media:credit><![CDATA[Future / Edited with Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Kitchen appliances on gradient background]]></media:description>                                                            <media:text><![CDATA[Kitchen appliances on gradient background]]></media:text>
                                <media:title type="plain"><![CDATA[Kitchen appliances on gradient background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/KexY8jZGHNutJaacVxKDe4-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Only seven days left before <a href="https://www.tomsguide.com/sales-events/fathers-day-gift-ideas-au">Father's Day 2026</a>, which falls on Sunday, September 6 in Australia.</p><p>If dad enjoys cooking up a feast, or maybe hosting the occasional dinner party, there's no better way to celebrate him than nabbing a kitchen-inspired gift this year. From small appliances to cookware and drinkware, there's plenty to consider here, as I've spotted 21 of the best kitchen and dining Father's Day gifts below. </p><p>All of these items have 4-star reviews on <a href="https://www.amazon.com.au/b/?_encoding=UTF8&node=222081923051&pd_rd_w=FBhNx&content-id=amzn1.sym.c75f719b-9ab0-435d-b82c-dec893d0af07&pf_rd_p=c75f719b-9ab0-435d-b82c-dec893d0af07&pf_rd_r=PFYM99CHZD98Q1J4GKH2&pd_rd_wg=EuT2g&pd_rd_r=f88e13df-cee3-4e46-a377-4ee7cc0d9fc6&ref_=pd_hp_d_r_stg_unk" target="_blank">Amazon</a>, <a href="https://kitchenaid.com.au/collections/sale" target="_blank">KitchenAid</a>, and <a href="https://www.davidjones.com/gifts/fathers-day" target="_blank">David Jones</a>, plus they offer same-day or fast delivery, so your preferred gift will arrive just in time for the big day. </p><p>I'll stop with the fuss about now — here are the best kitchen-inspired Father's Day gifts available right now. </p>        <div class="featured_product_block featured_block_hero" data-id="c1a88b3a-a294-11f1-ad60-0b5c83491949">            <a href="https://www.amazon.com.au/Breville-Toast-4-Slice-Sandwich-Press/dp/B0DB4FBBN8" data-model-name="the Toast and Melt 4-Slice Sandwich Press, Black Truffle" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:150%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/K6fcFkU8ABMKyZMqoGW3UF.jpg" alt="Breville the Toast and Melt 4-Slice Sandwich Press, Black Truffle, Lsg545btr"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Breville</div>                                        <div class="featured__title">the Toast and Melt 4-Slice Sandwich Press, Black Truffle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88bc6-a294-11f1-ae98-c520c23c781d">            <a href="https://www.amazon.com.au/Ninja-CleanCrispTM-Functionality-Leak-Proof-FN101SGANZ" data-model-name="Crispi Air fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ji967wLx3MW4p5HDTHCYr.jpg" alt="Ninja Crispi 4-In-1 Glass Air Fryer | With Non-Toxic 4qt & 6-Cup Glass Containers & Lids | Microwave, Freezer, Dishwasher Safe | 450°f Max Temp |1500 Watt | Green | Fn101sg"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ninja</div>                                        <div class="featured__title">Crispi Air fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88c48-a294-11f1-87e1-937bd3e660f0">            <a href="https://www.amazon.com.au/DeLonghi-Nespresso-Single-Serve-Centrifusion-Technology/dp/B0BSV6MRM6" data-model-name="Vertuo Pop, Pacific Blue" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/AXJpc6UQSuFnqkTzxfZxME.jpg" alt="De'longhi Nespresso Vertuo Pop Env90.a, Capsule Coffee Machine, Single-Serve Pod Coffee Machine, 4 Cup Sizes, Nespresso Starter Pack, Centrifusion Technology, Smart Connectivity, Pacific Blue"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nespresso</div>                                        <div class="featured__title">Vertuo Pop, Pacific Blue</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88cc0-a294-11f1-8678-0fd4b4f2a7aa">            <a href="https://www.amazon.com.au/Russell-Hobbs-21670-70-red-21670-70-Stainless/dp/B01J1RWURS" data-model-name="Kettle 1.7L" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/DYC8nLQgckrFk6ZqZreo4F.jpg" alt="Russell Hobbs Kettle [1.7 L, 2400 W] Retro Red and Polished Stainless Steel (quick Boil Function, Water Temperature Display in Retro Design, External Water Level Indicator) Vintage Tea Maker 21670-70"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Russell Hobbs</div>                                        <div class="featured__title">Kettle 1.7L</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88d42-a294-11f1-8b98-fde935f6a04c">            <a href="https://www.amazon.com.au/dreame-Portable-Blender-BPA-Free-Self-Cleaning/dp/B0H1C1FWS1" data-model-name="Portable Blender" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jLMNZRNpYRLB3GiFgnzsBE.jpg" alt="Dreame Portable Blender, 880 Ml Bpa-Free Cup, 5 Presets Self-Cleaning Green"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Dreame</div>                                        <div class="featured__title">Portable Blender</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88dba-a294-11f1-8111-27021b787040">            <a href="https://kitchenaid.com.au/products/kitchenaid-3-5-cup-mini-food-chopper-kfc3516a-1" data-model-name="3.5 Cup Mini Food Chopper" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:70.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/hFuK2rRP3Y3Ek6BVK3YqMF.jpg" alt="3.5 Cup Mini Food Chopper Kfc3516a"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KitchenAid</div>                                        <div class="featured__title">3.5 Cup Mini Food Chopper</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88e32-a294-11f1-a8af-8379444a3035">            <a href="https://www.amazon.com.au/CERA-Self-Heating-Portable-Espresso-CERApresso/dp/B0FHPS5LXC/ref=sr_1_14" data-model-name="Self Heating Portable Espresso Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/mQsroLpQbkgbC3ZVaNnyEE.jpg" alt="Cera+ Self Heating Portable Espresso Machine, 20 Bar Coffee Maker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>CERA+</div>                                        <div class="featured__title">Self Heating Portable Espresso Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88eaa-a294-11f1-b705-2d95cbfa0d87">            <a href="https://www.amazon.com.au/Bialetti-Express-Espresso-Maker-06799/dp/B0000AN3QI" data-model-name="Moka Express Stovetop Espresso Maker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/pFjtYdEq7g76bnKM5hL4xD.jpg" alt="Bialetti - Moka Express Stovetop Espresso Maker, 3 Cups (130 Ml) Capacity – Makes Real Italian Coffee – Iconic Moka Pot With Patented Safety Valve, Ergonomic Handle, Aluminium, Silver"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Bialetti</div>                                        <div class="featured__title">Moka Express Stovetop Espresso Maker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88f2c-a294-11f1-9343-c196cabfbec0">            <a href="https://www.amazon.com.au/Wirefree-Thermometer-INT-11I-B-Bluetooth-Waterproof/dp/B0GSQ6JPN7" data-model-name="Mini Wirefree Meat BBQ Thermometer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/FhqiRzkT8m3hz3D37XkvYF.jpg" alt="Inkbird Mini Wirefree Meat Bbq Thermometer Int-11i-B, Bluetooth 305m Wide Distance, 1s Fast Readout,built-In 500mah Lithium Battery Ip67 Waterproof 25 Min Fast Charge for Kitchen Bbq Grill Oven Smoker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Inkbird</div>                                        <div class="featured__title">Mini Wirefree Meat BBQ Thermometer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a88fa4-a294-11f1-88d2-1fc9e87d2cfd">            <a href="https://www.amazon.com.au/Russell-Hobbs-Slow-Cooker-Black/dp/B07D8RNVP1" data-model-name="7L Slow Cooker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/YmDaRNLqgrZpbrjVETCi4E.jpg" alt="Russell Hobbs 7l Slow Cooker, Rhsc7, 3 Heat Settings (low, High and Keep Warm), Dishwasher-Safe Ceramic Pot, Built-In Lid Holder, Cooks 8 Servings - Matte Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Russell Hobbs</div>                                        <div class="featured__title">7L Slow Cooker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a8901c-a294-11f1-b893-77ad33e72ad8">            <a href="https://www.amazon.com.au/Scanpan-Impact-Frypan-20cm-28cm/dp/B077P2X216" data-model-name="Impact Stainless Steel Fry Pan Set 2pk" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:67.80%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/NL6AZoyN5KcXPvJHF5dY7E.jpg" alt="Scanpan Impact 2-Piece Stainless Steel Fry Pan Set, 20 Cm & 28 Cm"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Scanpan</div>                                        <div class="featured__title">Impact Stainless Steel Fry Pan Set 2pk</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a89094-a294-11f1-b0f7-917703e79ee4">            <a href="https://www.amazon.com.au/Tefal-Non-Stick-C2893802-Aluminium-Technology/dp/B0CKJ98P83" data-model-name="Daily Expert Red Non-Stick Pancake Pan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/UYrtuasmjhDnXm9ijbYy7F.jpg" alt="Tefal Daily Expert Red Non-Stick Pancake Pan, 25cm, C2893802, Fixed Handle Aluminium, Titanium Non-Stick Coating, Thermo Signal Technology for Easier Cooking, Suitable for All Cooktops, Oven Safe"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Tefal</div>                                        <div class="featured__title">Daily Expert Red Non-Stick Pancake Pan</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a8910c-a294-11f1-960a-85c111315b52">            <a href="https://www.amazon.com.au/Tefal-H912S217-Induction-Non-Stick-Anodised/dp/B08F5FHFHZ/" data-model-name="Jamie Oliver by Tefal Cooks Classic Non-Stick Frypan 2pk" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/vMoYTpu9ZP3QZZQ6FuDTJE.jpg" alt="Tefal Jamie Oliver by Tefal Cooks Classic Non-Stick Induction Hard Anodised Twinpack Frypan 24/28cm, H912s217"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Tefal</div>                                        <div class="featured__title">Jamie Oliver by Tefal Cooks Classic Non-Stick Frypan 2pk</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a8918e-a294-11f1-8f94-8166f1c5d089">            <a href="https://www.amazon.com.au/Corelle-Livingware-18-Piece-Dinnerware-1088609/dp/B00F39GFUW/ref=sr_1_1" data-model-name="Winter Frost White Dinnerware Set, 18-Piece" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/xmi2LzHp76FSPcQk4oh3BF.jpg" alt="Corelle Winter Frost White Dinnerware Set, 18-Piece – Chip-Resistant Triple-Layer Glass – Stackable Design for Everyday Dining, 6 Dinner Plates, 6 Bread & Butter Plates, 4 Soup/cereal Bowls"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Corelle</div>                                        <div class="featured__title">Winter Frost White Dinnerware Set, 18-Piece</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a89206-a294-11f1-8352-b516830da536">            <a href="https://www.amazon.com.au/Multi-Prep-Compact-4pc-Prep-Mcolor/dp/B0CF6B979W/" data-model-name="Multi-Prep Compact 4-In-1 Multifunctional Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:81.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/aH9quCbNdCBLushj2sREWE.jpg" alt="Joseph Joseph Multi-Prep Compact 4-In-1 Multifunctional Set, Kitchen Vegetable Chopper & Dicer, Cheese Grater, Mandoline Veggie Slicer, Food Julienne Cutter With Container, Stainless Steel Blades"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Multi-Prep Compact 4-In-1 Multifunctional Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a8927e-a294-11f1-b0e6-e53adc173fd2">            <a href="https://www.amazon.com.au/Joseph-Colander-Strainer-Vegetable-Space-Saving/dp/B0DPG6JG3K" data-model-name="Droplet Mini Colander With Folding Handles" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:81.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/CeYv85aBMMtAFpuDZSCzdF.jpg" alt="Joseph Joseph Droplet Mini Colander With Folding Handles, Kitchen Strainer for Small Vegetable, Fruits & Berries, Space-Saving"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Droplet Mini Colander With Folding Handles</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a89300-a294-11f1-96c6-9b95201bdf3c">            <a href="https://www.amazon.com.au/Whiskey-Smoker-Kit-Torch-Fashioned/dp/B0BJV68C17" data-model-name="Whiskey Smoker Kit With Torch" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rTmtej8Z2JJXuFp3sXAWSE.jpg" alt="Whiskey Smoker Kit With Torch - 6 Flavors Wood Chips, 2 Glasses, 2 Ice Ball Molds - Cocktail Smoker Infuser Kit, Old Fashioned Drink Smoker Kit, Birthday Bourbon Whiskey Gifts for Men,dad(no Butane)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>ComboJoy</div>                                        <div class="featured__title">Whiskey Smoker Kit With Torch</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a89378-a294-11f1-93b1-cbe5dc88828b">            <a href="https://www.amazon.com.au/JoyJolt-Decanter-Lead-free-Crystal-Carafe/dp/B073NXQWBV/" data-model-name="Cigno Wine Decanter" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/qNmdy9ESusQDw9wfdYBe2F.jpg" alt="Joyjolt Cigno Wine Decanter Hand Blown Lead-Free Crystal Glass Red Wine Carafe 50.72 Fl. Oz."></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>JoyJolt</div>                                        <div class="featured__title">Cigno Wine Decanter</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a893f0-a294-11f1-952c-99681b5b87c9">            <a href="https://www.davidjones.com/product/gentlemens-hardware-top-hat-bottle-stoppers-set-of-2-28768167" data-model-name="Top Hat Bottle Stoppers - Set of 2" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:128.79%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/fXbMJwjuvpEfhYjyHxLhfU.png" alt="Top hat bottle tops"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Gentleman's Hardware</div>                                        <div class="featured__title">Top Hat Bottle Stoppers - Set of 2</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a89468-a294-11f1-b9db-b9707d1bf769">            <a href="https://www.davidjones.com/product/gentlemens-hardware-golf-ball-bottle-opener-28768171" data-model-name="Golf Ball Bottle Opener" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:128.79%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/AdgBT6PdKyFY9GZQFH82zP.png" alt="Gentleman's Hardware golf ball"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Gentleman's Hardware</div>                                        <div class="featured__title">Golf Ball Bottle Opener</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c1a894ea-a294-11f1-934b-31590118f811">            <a href="https://www.davidjones.com/product/gentlemens-hardware-fish-hip-flask-26067577" data-model-name="Fish Hip Flask" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:130.18%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/9gTVk8dqEaztEfXZzonhE7.png" alt="Gentleman's hardware fish flask"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Gentleman's Hardware</div>                                        <div class="featured__title">Fish Hip Flask</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/wellness/forget-socks-shop-these-30-expert-picked-skincare-and-haircare-buys-for-dad-this-fathers-day">Forget socks — shop these 30 expert-picked skincare and haircare buys for dad this Father's Day</a></li><li><a href="https://www.tomsguide.com/tech/forget-a-new-tie-ive-found-15-of-the-best-fathers-day-tech-gifts-that-will-make-you-his-favourite-child">Forget a new tie — I've found 15 of the best Father's Day tech gifts that will make you his favourite child</a></li><li><a href="https://www.tomsguide.com/gaming/gaming-peripherals/gamer-dad-ive-found-11-winning-fathers-day-gaming-ideas-to-level-up-your-gifting">Gamer dad? I've found 11 winning Father's Day gaming ideas to level up your gifting</a></li><li><a href="https://www.tomsguide.com/wellness/fitness/run-dont-walk-ive-spotted-9-of-the-best-fitness-fathers-day-gifts-thatll-help-dad-work-on-his-dadbod">Run, don't walk — I've spotted 9 of the best fitness Father's Day gifts that'll help dad work on his #DadBod</a></li><li><a href="https://www.tomsguide.com/home/gardening/got-an-outdoorsy-or-garden-loving-dad-ive-compiled-12-of-the-best-fathers-day-gifts-that-your-green-thumbed-dad-would-enjoy">Got an outdoorsy or garden-loving dad? I've compiled 12 of the best Father's Day gifts that your green-thumbed dad would enjoy</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested Philips’s new ‘budget’ Baristina espresso machine — but I’m more confused than impressed ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Philips Baristina is an interesting espresso machine. It’s an automatic, but also not quite an automatic. How does that work, I hear you ask? Let me explain. </p><p>So, while it grinds, doses, tamps, and brews without any input from you (aside from you clicking the shot button), you also have to empty the portafilter. Isn’t that the worst part of any manual machine? The coffee grounds always get stuck to my hands. Anyway, if you’re after an espresso machine that does <em>most</em> of the hard work for you, the Baristina could be ideal. </p><p>However, the espresso does taste pretty mid, and the price is about $200 more than I’d expect. So, could it be one of the <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">best espresso machines</a>? Hm. I don’t think so. But want to find out the full story? Keep reading this Philips Baristina review.</p><h2 class="article-body__section" id="section-philips-baristina-review-specs"><span>Philips Baristina review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Baristina-Espresso-Automatic-Portafilter-BAR302/dp/B0F6TQYRJV" target="_blank" rel="nofollow">$449-$529</a> / <a href="https://www.amazon.co.uk/Baristina-Automatic-Portafilter-BAR300-60/dp/B0GX61DZGS" target="_blank" rel="nofollow">£299-£379</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>11 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>15 x 7 x 13.6 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Grinder</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Heating system </strong></p></td><td  ><p>Unspecified </p></td></tr><tr><td class="firstcol " ><p><strong>Pressure </strong></p></td><td  ><p>16 bar</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank capacity</strong></p></td><td  ><p>40.6 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>None</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-philips-baristina-review-price-availability"><span>Philips Baristina review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="Ckgfg6oQ5wGpZnuVfNrcSW" name="Philips_Baristina_10" alt="the philips baristina photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Ckgfg6oQ5wGpZnuVfNrcSW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Philips Baristina is <a href="https://www.amazon.com/Baristina-Espresso-Automatic-Portafilter-BAR302/dp/B0F6TQYRJV" target="_blank" rel="nofollow">$449-$529 from Amazon U.S.</a> and <a href="https://www.amazon.co.uk/Baristina-Automatic-Portafilter-BAR300-60/dp/B0GX61DZGS" target="_blank" rel="nofollow">£299-£379 from Amazon U.K</a>. While this price is reasonable enough in the U.K., I would not recommend paying $529 if you’re in the U.S. You don’t even get a built-in milk frother — it looks and feels like a Nespresso Aeroccino standalone milk heater/frother. You can’t customize milk texture at all. </p><p>Even though I think the Baristina is overpriced, it’s an automatic machine, which means it’s always going to be pricier than manuals. However, I would wholeheartedly recommend spending just a little more for a genuinely decent machine. </p><p>Instead of spending $529 on what is effectively an espresso maker and a separate (cheap-feeling, one-trick-pony) milk frother, I’d spend $699 on the <a href="https://www.amazon.com/PHILIPS-Automatic-Espresso-Machine-LatteGo/dp/B0GZW8QCGM" target="_blank" rel="nofollow">Philips 2300 Series</a>, $799 on the <a href="https://www.tomsguide.com/home/coffee-makers/kitchenaid-kf2-review-this-fully-automatic-coffee-machine-effortlessly-takes-the-guesswork-out-of-your-morning-brew" target="_blank">KitchenAid KF2</a>, or, heck, even $379 on the <a href="https://www.tomsguide.com/home/coffee-makers/nespresso-citiz-aeroccino-review">Nespresso CitiZ + Aeroccino</a>. It’s a pod machine, sure, but honestly? It makes a better brew. </p><h2 class="article-body__section" id="section-philips-baristina-review-design"><span>Philips Baristina review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="y7T39v8dU9cq7iCocsQvLW" name="Philips_Baristina_9" alt="the philips baristina photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/y7T39v8dU9cq7iCocsQvLW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>So, I’m not a huge fan of how the Baristina looks. I think it looks incredibly cheap, like something that costs $200 rather than $530. When I found out how much it costs, I actually gasped. </p><p>While it doesn’t look <em>bad</em>, it does look <em>cheap</em>. Whether this is a dealbreaker will depend on your preferences. For me, I’d rather spend a little extra on the KitchenAid KF2 or just get a bog-standard manual like the $300 <a href="https://www.tomsguide.com/home/coffee-makers/sage-breville-bambino-review">Breville Bambino</a>. </p><p>You get a “portafilter”. I say “portafilter” because, yes, it’s technically a portafilter in that it has a handle and a basket where the coffee goes. But you can’t use it like a traditional portafilter — you don’t dose and tamp manually. All you have to do is push the portafilter over to the grinder side at the start of the brew and then sit back and relax. </p><p>While, yes, I <em>loved </em>watching the portafilter snap back into place after the grinder finished (it’s the simple things), I think this is more of a style gimmick than actual function. If you’re after a truly automatic machine, it might even be a major con. Yes, you have to empty and clean out the portafilter yourself. </p><h2 class="article-body__section" id="section-philips-baristina-review-espresso"><span>Philips Baristina review: Espresso</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="4aBpHB3mhJV5FmajBR7VjV" name="Philips_Baristina_6" alt="the philips baristina photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/4aBpHB3mhJV5FmajBR7VjV.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you’re an espresso aficionado, you won’t want to read this. However, if you’re someone who prioritizes speed, ease of use, and simplicity, you’re the perfect candidate for a Baristina. </p><p>As it’s a bean-to-cup automatic — despite having a “portafilter” — you have very little input. Pour your beans in, press the espresso button, slide the portafilter to the grinder side (which is super fun), and you’ve done all the work. The machine snaps the portafilter into place hands-free, and, yes, this is mesmerizing to watch. I don’t care how lame that makes me; I enjoy watching a portafilter careen across a coffee machine like a stone from a slingshot. It’s novel. Sue me. </p><p>In terms of espresso flavor… it’s okay. It tastes incredibly average. I tried the machine with both artisan Ethiopian beans and your generic “strong” dark roast, and it tasted better with the latter. I’d recommend using cheaper, darker beans on this machine, as the excessive 16-bar pressure and the tiny (40mm) pressurized portafilter destroys any nuance anyway. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3256px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="rtiKZ2LC6EDh7AA5Dtja4Y" name="philpis baristina espresso" alt="a photo of a drink made on the philips baristina espresso machine" src="https://cdn.mos.cms.futurecdn.net/rtiKZ2LC6EDh7AA5Dtja4Y.jpg" mos="" align="middle" fullscreen="" width="3256" height="1832" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, this espresso shot is foamy and aerated, which means it was extracted under too much pressure. The crema is fluffy, but it dissipated within seconds. The first Ethiopian beans (which usually taste divine) were overly sour and astringent. However, the second generic Illy-brand beans resulted in a stronger, more palatable coffee taste. Of course, this won’t impress coffee nerds, but this isn’t a machine for coffee nerds. </p><p>Even so, I struggle to recommend this machine wholeheartedly considering you can get much better machines for just a little extra. The <a href="https://www.amazon.com/PHILIPS-Automatic-Espresso-Machine-LatteGo/dp/B0GZW8QCGM">Philips 2300</a> is a modest stretch at $699; the <a href="https://www.tomsguide.com/home/coffee-makers/kitchenaid-kf2-review-this-fully-automatic-coffee-machine-effortlessly-takes-the-guesswork-out-of-your-morning-brew">KitchenAid KF2</a> is $799. Considering these machines have built-in milk preparation and zero user input, I’d recommend them if you’re after a totally automatic machine. The espresso tastes much nicer. </p><h2 class="article-body__section" id="section-philips-baristina-review-grinder"><span>Philips Baristina review: Grinder</span></h2><p>While the Philips Baristina does have a built-in grinder and therefore demands a standalone ‘grinder’ section, this is going to be extremely short. </p><p>The grinder is not adjustable. Yes, you heard me correctly. Not adjustable. The grinder grinds to one size and one size only. Want it finer? Not possible. Coarser? Oh, again, you guessed it, not possible. </p><p>Other automatic machines such as the De’Longhi Rivelia have an adjustable grinder, and also the $699 manual bean-to-cup De’Longhi Arte Evo. If you’re interested in actually playing and experimenting with coffee, I’d recommend the Arte Evo over the Baristina. You’re paying an extra $150, but it’s often discounted to $599 (so just $50 more), and you get all the bonuses of a manual machine with the ease of an assisted tamping system. </p><h2 class="article-body__section" id="section-philips-baristina-review-milk"><span>Philips Baristina review: Milk</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="MfszB5jgQRneGjDEThJMeV" name="Philips_Baristina_11" alt="the philips baristina photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/MfszB5jgQRneGjDEThJMeV.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As mentioned earlier, the Baristina comes in two variants: with milk frother and without milk frother. If you order the cheaper model, it won’t come with the frother. </p><p>Those familiar with something like the Nespresso Aeroccino will recognize this milk frother: the Philips model is essentially just your standard countertop model. It’s not particularly unique or innovative, but damn, it works. If it ain’t broke, don’t fix, right? </p><p>Here’s a photo of a milk drink made. Notice how I said “milk drink” rather than latte or cappuccino — the Baristina can only make one type of milk. No wet lattes or dry cappuccinos here. Milk or no milk only!</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3464px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="5kq8rRgCmJq6oKjjjKcnxX" name="philpis baristina milk" alt="a photo of a drink made on the philips baristina espresso machine" src="https://cdn.mos.cms.futurecdn.net/5kq8rRgCmJq6oKjjjKcnxX.jpg" mos="" align="middle" fullscreen="" width="3464" height="1948" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the milk is extremely thick. Don’t get me wrong, the texture is satisfying, with well-distributed, evenly sized bubbles. However, this isn’t going to impress people who are used to barista-crafted beverages. It cannot make microfoam; therefore, drinks like lattes and flat whites are out of the question. You can only make cappuccinos. </p><h2 class="article-body__section" id="section-philips-baristina-review-storage-maintenance"><span>Philips Baristina review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="GXChiJ6pWNhcLoZd4fLDhV" name="Philips_Baristina_7" alt="the philips baristina photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/GXChiJ6pWNhcLoZd4fLDhV.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Although 15 inches tall, the Philips Baristina feels a lot smaller. This height is solely due to the grinder hopper — its main body is more compact. Of course, you’d need to ensure you have space under your counters for the hopper, but the machine should take up less space than you think. </p><p>Due to this size, the machine has a teeny-tiny drip tray. I always recommend cleaning out your drip tray after every use, but in this case, it’s definitely a necessity. If you don’t clean it out every day, it’s going to overflow before you know it. </p><p>As with the rest of the machine, the drip tray is incredibly plasticky. This is one of my biggest complaints — a design this plasticky is pretty inexcusable when you’re charging $549 from an aesthetic and longevity perspective. </p><p>Speaking of longevity, you get a 2-year warranty as standard, which is the same as Breville, Ninja, and De’Longhi. </p><h2 class="article-body__section" id="section-philips-baristina-review-verdict"><span>Philips Baristina review: Verdict</span></h2><p>While I don’t think the Philips Baristina is the worst espresso machine I’ve ever used, I can’t recommend it wholeheartedly. Only a very specific user base should get this machine. If you can’t stretch to $699 for a Philips 2300 Series, you don’t mind mid-tasting espresso, you’re happy with one-size-fits-all milk texture, <em>and</em> you’re happy to spend $200 more than I think you should, then yes, this would be the machine for you. </p><p>All other people, though? You’d either be better off with the $699 Philips 2300 Series, the $799 KitchenAid KF2, or even venture into the world of manual bean-to-cups with the fantastic De’Longhi Arte Evo. </p><p>As I said earlier, if you want simplicity and ease of use over anything else, I’d even go as far to say you’re the perfect candidate for a Nespresso CitiZ + Milk. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/philips-baristina-review</link>
                                                                            <description>
                            <![CDATA[ The Philips Baristina is a curious espresso machine, with a handheld portafilter yet a totally automated grinding and dosing system. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">CVW7DNBjaXvgJqB9dECYDK</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/JE2SiR75byHZm6XMWGCq3W-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 28 Aug 2026 09:52:38 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/JE2SiR75byHZm6XMWGCq3W-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the philips baristina photographed against the blue tom&#039;s guide background]]></media:description>                                                            <media:text><![CDATA[the philips baristina photographed against the blue tom&#039;s guide background]]></media:text>
                                <media:title type="plain"><![CDATA[the philips baristina photographed against the blue tom&#039;s guide background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/JE2SiR75byHZm6XMWGCq3W-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Philips Baristina is an interesting espresso machine. It’s an automatic, but also not quite an automatic. How does that work, I hear you ask? Let me explain. </p><p>So, while it grinds, doses, tamps, and brews without any input from you (aside from you clicking the shot button), you also have to empty the portafilter. Isn’t that the worst part of any manual machine? The coffee grounds always get stuck to my hands. Anyway, if you’re after an espresso machine that does <em>most</em> of the hard work for you, the Baristina could be ideal. </p><p>However, the espresso does taste pretty mid, and the price is about $200 more than I’d expect. So, could it be one of the <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">best espresso machines</a>? Hm. I don’t think so. But want to find out the full story? Keep reading this Philips Baristina review.</p><h2 class="article-body__section" id="section-philips-baristina-review-specs"><span>Philips Baristina review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Baristina-Espresso-Automatic-Portafilter-BAR302/dp/B0F6TQYRJV" target="_blank" rel="nofollow">$449-$529</a> / <a href="https://www.amazon.co.uk/Baristina-Automatic-Portafilter-BAR300-60/dp/B0GX61DZGS" target="_blank" rel="nofollow">£299-£379</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>11 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>15 x 7 x 13.6 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Grinder</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Heating system </strong></p></td><td  ><p>Unspecified </p></td></tr><tr><td class="firstcol " ><p><strong>Pressure </strong></p></td><td  ><p>16 bar</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank capacity</strong></p></td><td  ><p>40.6 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>None</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-philips-baristina-review-price-availability"><span>Philips Baristina review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="Ckgfg6oQ5wGpZnuVfNrcSW" name="Philips_Baristina_10" alt="the philips baristina photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Ckgfg6oQ5wGpZnuVfNrcSW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Philips Baristina is <a href="https://www.amazon.com/Baristina-Espresso-Automatic-Portafilter-BAR302/dp/B0F6TQYRJV" target="_blank" rel="nofollow">$449-$529 from Amazon U.S.</a> and <a href="https://www.amazon.co.uk/Baristina-Automatic-Portafilter-BAR300-60/dp/B0GX61DZGS" target="_blank" rel="nofollow">£299-£379 from Amazon U.K</a>. While this price is reasonable enough in the U.K., I would not recommend paying $529 if you’re in the U.S. You don’t even get a built-in milk frother — it looks and feels like a Nespresso Aeroccino standalone milk heater/frother. You can’t customize milk texture at all. </p><p>Even though I think the Baristina is overpriced, it’s an automatic machine, which means it’s always going to be pricier than manuals. However, I would wholeheartedly recommend spending just a little more for a genuinely decent machine. </p><p>Instead of spending $529 on what is effectively an espresso maker and a separate (cheap-feeling, one-trick-pony) milk frother, I’d spend $699 on the <a href="https://www.amazon.com/PHILIPS-Automatic-Espresso-Machine-LatteGo/dp/B0GZW8QCGM" target="_blank" rel="nofollow">Philips 2300 Series</a>, $799 on the <a href="https://www.tomsguide.com/home/coffee-makers/kitchenaid-kf2-review-this-fully-automatic-coffee-machine-effortlessly-takes-the-guesswork-out-of-your-morning-brew" target="_blank">KitchenAid KF2</a>, or, heck, even $379 on the <a href="https://www.tomsguide.com/home/coffee-makers/nespresso-citiz-aeroccino-review">Nespresso CitiZ + Aeroccino</a>. It’s a pod machine, sure, but honestly? It makes a better brew. </p><h2 class="article-body__section" id="section-philips-baristina-review-design"><span>Philips Baristina review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="y7T39v8dU9cq7iCocsQvLW" name="Philips_Baristina_9" alt="the philips baristina photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/y7T39v8dU9cq7iCocsQvLW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>So, I’m not a huge fan of how the Baristina looks. I think it looks incredibly cheap, like something that costs $200 rather than $530. When I found out how much it costs, I actually gasped. </p><p>While it doesn’t look <em>bad</em>, it does look <em>cheap</em>. Whether this is a dealbreaker will depend on your preferences. For me, I’d rather spend a little extra on the KitchenAid KF2 or just get a bog-standard manual like the $300 <a href="https://www.tomsguide.com/home/coffee-makers/sage-breville-bambino-review">Breville Bambino</a>. </p><p>You get a “portafilter”. I say “portafilter” because, yes, it’s technically a portafilter in that it has a handle and a basket where the coffee goes. But you can’t use it like a traditional portafilter — you don’t dose and tamp manually. All you have to do is push the portafilter over to the grinder side at the start of the brew and then sit back and relax. </p><p>While, yes, I <em>loved </em>watching the portafilter snap back into place after the grinder finished (it’s the simple things), I think this is more of a style gimmick than actual function. If you’re after a truly automatic machine, it might even be a major con. Yes, you have to empty and clean out the portafilter yourself. </p><h2 class="article-body__section" id="section-philips-baristina-review-espresso"><span>Philips Baristina review: Espresso</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="4aBpHB3mhJV5FmajBR7VjV" name="Philips_Baristina_6" alt="the philips baristina photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/4aBpHB3mhJV5FmajBR7VjV.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you’re an espresso aficionado, you won’t want to read this. However, if you’re someone who prioritizes speed, ease of use, and simplicity, you’re the perfect candidate for a Baristina. </p><p>As it’s a bean-to-cup automatic — despite having a “portafilter” — you have very little input. Pour your beans in, press the espresso button, slide the portafilter to the grinder side (which is super fun), and you’ve done all the work. The machine snaps the portafilter into place hands-free, and, yes, this is mesmerizing to watch. I don’t care how lame that makes me; I enjoy watching a portafilter careen across a coffee machine like a stone from a slingshot. It’s novel. Sue me. </p><p>In terms of espresso flavor… it’s okay. It tastes incredibly average. I tried the machine with both artisan Ethiopian beans and your generic “strong” dark roast, and it tasted better with the latter. I’d recommend using cheaper, darker beans on this machine, as the excessive 16-bar pressure and the tiny (40mm) pressurized portafilter destroys any nuance anyway. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3256px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="rtiKZ2LC6EDh7AA5Dtja4Y" name="philpis baristina espresso" alt="a photo of a drink made on the philips baristina espresso machine" src="https://cdn.mos.cms.futurecdn.net/rtiKZ2LC6EDh7AA5Dtja4Y.jpg" mos="" align="middle" fullscreen="" width="3256" height="1832" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, this espresso shot is foamy and aerated, which means it was extracted under too much pressure. The crema is fluffy, but it dissipated within seconds. The first Ethiopian beans (which usually taste divine) were overly sour and astringent. However, the second generic Illy-brand beans resulted in a stronger, more palatable coffee taste. Of course, this won’t impress coffee nerds, but this isn’t a machine for coffee nerds. </p><p>Even so, I struggle to recommend this machine wholeheartedly considering you can get much better machines for just a little extra. The <a href="https://www.amazon.com/PHILIPS-Automatic-Espresso-Machine-LatteGo/dp/B0GZW8QCGM">Philips 2300</a> is a modest stretch at $699; the <a href="https://www.tomsguide.com/home/coffee-makers/kitchenaid-kf2-review-this-fully-automatic-coffee-machine-effortlessly-takes-the-guesswork-out-of-your-morning-brew">KitchenAid KF2</a> is $799. Considering these machines have built-in milk preparation and zero user input, I’d recommend them if you’re after a totally automatic machine. The espresso tastes much nicer. </p><h2 class="article-body__section" id="section-philips-baristina-review-grinder"><span>Philips Baristina review: Grinder</span></h2><p>While the Philips Baristina does have a built-in grinder and therefore demands a standalone ‘grinder’ section, this is going to be extremely short. </p><p>The grinder is not adjustable. Yes, you heard me correctly. Not adjustable. The grinder grinds to one size and one size only. Want it finer? Not possible. Coarser? Oh, again, you guessed it, not possible. </p><p>Other automatic machines such as the De’Longhi Rivelia have an adjustable grinder, and also the $699 manual bean-to-cup De’Longhi Arte Evo. If you’re interested in actually playing and experimenting with coffee, I’d recommend the Arte Evo over the Baristina. You’re paying an extra $150, but it’s often discounted to $599 (so just $50 more), and you get all the bonuses of a manual machine with the ease of an assisted tamping system. </p><h2 class="article-body__section" id="section-philips-baristina-review-milk"><span>Philips Baristina review: Milk</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="MfszB5jgQRneGjDEThJMeV" name="Philips_Baristina_11" alt="the philips baristina photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/MfszB5jgQRneGjDEThJMeV.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As mentioned earlier, the Baristina comes in two variants: with milk frother and without milk frother. If you order the cheaper model, it won’t come with the frother. </p><p>Those familiar with something like the Nespresso Aeroccino will recognize this milk frother: the Philips model is essentially just your standard countertop model. It’s not particularly unique or innovative, but damn, it works. If it ain’t broke, don’t fix, right? </p><p>Here’s a photo of a milk drink made. Notice how I said “milk drink” rather than latte or cappuccino — the Baristina can only make one type of milk. No wet lattes or dry cappuccinos here. Milk or no milk only!</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3464px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="5kq8rRgCmJq6oKjjjKcnxX" name="philpis baristina milk" alt="a photo of a drink made on the philips baristina espresso machine" src="https://cdn.mos.cms.futurecdn.net/5kq8rRgCmJq6oKjjjKcnxX.jpg" mos="" align="middle" fullscreen="" width="3464" height="1948" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the milk is extremely thick. Don’t get me wrong, the texture is satisfying, with well-distributed, evenly sized bubbles. However, this isn’t going to impress people who are used to barista-crafted beverages. It cannot make microfoam; therefore, drinks like lattes and flat whites are out of the question. You can only make cappuccinos. </p><h2 class="article-body__section" id="section-philips-baristina-review-storage-maintenance"><span>Philips Baristina review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="GXChiJ6pWNhcLoZd4fLDhV" name="Philips_Baristina_7" alt="the philips baristina photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/GXChiJ6pWNhcLoZd4fLDhV.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Although 15 inches tall, the Philips Baristina feels a lot smaller. This height is solely due to the grinder hopper — its main body is more compact. Of course, you’d need to ensure you have space under your counters for the hopper, but the machine should take up less space than you think. </p><p>Due to this size, the machine has a teeny-tiny drip tray. I always recommend cleaning out your drip tray after every use, but in this case, it’s definitely a necessity. If you don’t clean it out every day, it’s going to overflow before you know it. </p><p>As with the rest of the machine, the drip tray is incredibly plasticky. This is one of my biggest complaints — a design this plasticky is pretty inexcusable when you’re charging $549 from an aesthetic and longevity perspective. </p><p>Speaking of longevity, you get a 2-year warranty as standard, which is the same as Breville, Ninja, and De’Longhi. </p><h2 class="article-body__section" id="section-philips-baristina-review-verdict"><span>Philips Baristina review: Verdict</span></h2><p>While I don’t think the Philips Baristina is the worst espresso machine I’ve ever used, I can’t recommend it wholeheartedly. Only a very specific user base should get this machine. If you can’t stretch to $699 for a Philips 2300 Series, you don’t mind mid-tasting espresso, you’re happy with one-size-fits-all milk texture, <em>and</em> you’re happy to spend $200 more than I think you should, then yes, this would be the machine for you. </p><p>All other people, though? You’d either be better off with the $699 Philips 2300 Series, the $799 KitchenAid KF2, or even venture into the world of manual bean-to-cups with the fantastic De’Longhi Arte Evo. </p><p>As I said earlier, if you want simplicity and ease of use over anything else, I’d even go as far to say you’re the perfect candidate for a Nespresso CitiZ + Milk. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ These 15 Ninja deals are too good to miss ahead of Labor Day — save up to 40% on blenders, air fryers and more ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Labor Day is almost here — and if you're hosting any backyard barbecues or celebrations, chances are you could use a little help. That's where Ninja comes into play! The kitchen appliance brand has everything you need to meal prep and get grilling! </p><p>Fortunately, <a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow">Amazon is closing out the summer with a bunch of Ninja appliance deals</a>. From high-quality blenders and air fryers to pizza ovens and grills, there are some discounts here that you definitely don't want to miss — especially since the best deals are currently up to 40% off.</p><p>I've been scouring the sale and have found the best cooking deals to help you in the kitchen this Labor Day and beyond. I've listed all my favorites below, so keep scrolling to check them out.</p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow"><strong>shop all Ninja deals at Amazon</strong></a></li><li><strong>Ninja Blast Max: </strong><a href="https://www.amazon.com/dp/B0DB964918?" target="_blank" rel="nofollow"><strong>was $99 now $69</strong></a></li><li><strong>Ninja Iced Coffee Maker: </strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LNKN4Z?" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Ninja Crispi 4-in-1 Portable Glass Air Fryer: </strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><strong>was $179 now $159</strong></a></li><li><strong>Ninja Double Oven, 12-in-1: </strong><a href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow"><strong>was $379 now $219</strong></a></li><li><strong>Ninja Woodfire Oven & Smoker: </strong><a href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow"><strong>was $399 now $279</strong></a></li><li><strong>Ninja Luxe Café Premier: </strong><a href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow"><strong>now $599</strong></a></li><li><strong>Ninja FlexFlame Propane Grill: </strong><a href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0GJ76517J" target="_blank" rel="nofollow"><strong>was $1,119 now $999</strong></a></li></ul><h3 class="article-body__section" id="section-ninja-deals"><span>Ninja Deals</span></h3><div class="product"><a data-dimension112="69b0fedc-a21f-11f1-8a6e-418eb9d3d485" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69" href="https://www.amazon.com/dp/B0DB964918?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1216px;"><p class="vanilla-image-block" style="padding-top:123.36%;"><img id="9Q4QY399FaozAFUt4j8dTW" name="ninja-blast-max-portable-blender--twist--bb517479-54ce-4a77-974c-58e2f44dde33.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/9Q4QY399FaozAFUt4j8dTW.jpg" mos="" align="middle" fullscreen="" width="1216" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Blast Max is capable of taking on any challenge you throw at it, including crushing ice and blitzing berries to make yummy smoothies. It comes with multiple blend settings to make sure your blender is using the right level of power for the task at hand, and the compact size means it’s a great option for anyone with a small kitchen. <a class="view-deal button" href="https://www.amazon.com/dp/B0DB964918?" target="_blank" rel="nofollow" data-dimension112="69b0fedc-a21f-11f1-8a6e-418eb9d3d485" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69">View Deal</a></p></div><div class="product"><a data-dimension112="69b0ff4a-a21f-11f1-9b87-d7697e968c6e" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$79" href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B0GH8PVFHS?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:162.30%;"><img id="sFftzctqbej7oaSQLEtBRj" name="Ninja Pro Blender" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sFftzctqbej7oaSQLEtBRj.jpg" mos="" align="middle" fullscreen="" width="679" height="1102" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B0GH8PVFHS?" target="_blank" rel="nofollow" data-dimension112="69b0ff4a-a21f-11f1-9b87-d7697e968c6e" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$79">Professional Blender: was $109 now $79</a></strong><br>Be it daily smoothies or making ice cream, this blender can tackle it all, at the touch of a button. Also featured on our <a href="https://www.tomsguide.com/best-picks/best-blenders">best blenders</a> guide, this comes with 3 preset Auto-iQ programs that creates smoothies, frozen drinks, and ice cream, and its powerful blades can crush ice in no time. This is a great deal if you want an easy-to-use blender, with professional results.  <a class="view-deal button" href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B0GH8PVFHS?" target="_blank" rel="nofollow" data-dimension112="69b0ff4a-a21f-11f1-9b87-d7697e968c6e" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$79">View Deal</a></p></div><div class="product"><a data-dimension112="69b0ffc2-a21f-11f1-b219-a33e5661b9bb" data-action="Deal Block" data-label="Air Fryer Pro 4-in-1" data-dimension48="Air Fryer Pro 4-in-1" data-dimension25="$89" href="https://www.amazon.com/dp/B0CSZ7WBYW" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:560px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="f49Q8qGkV5TG2EsuDo3or5" name="Ninja Air Fryer Pro 4 in 1" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/f49Q8qGkV5TG2EsuDo3or5.jpg" mos="" align="middle" fullscreen="" width="560" height="560" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Air Fryer Pro 4-in-1 is a versatile kitchen appliance that air fries, roasts, reheats, and dehydrates. With its 5-quart capacity, it cooks food quickly using hot air circulation, delivering crispy, healthier meals with little to no oil. That makes it <em>perfect </em>for everyday cooking and batch meal prep!<a class="view-deal button" href="https://www.amazon.com/dp/B0CSZ7WBYW" target="_blank" rel="nofollow" data-dimension112="69b0ffc2-a21f-11f1-b219-a33e5661b9bb" data-action="Deal Block" data-label="Air Fryer Pro 4-in-1" data-dimension48="Air Fryer Pro 4-in-1" data-dimension25="$89">View Deal</a></p></div><div class="product"><a data-dimension112="69b10030-a21f-11f1-87c3-c57969611a45" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LP9SF1?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:99.56%;"><img id="4uiaRXRKY8Go6F4ZpM7UdA" name="Ninja coffee" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4uiaRXRKY8Go6F4ZpM7UdA.jpg" mos="" align="middle" fullscreen="" width="679" height="676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LP9SF1?" target="_blank" rel="nofollow" data-dimension112="69b10030-a21f-11f1-87c3-c57969611a45" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">Pods & Grounds Specialty Single-Serve Iced Coffee Maker: was $129 now $99</a></strong><br>This stylish Ninja coffee maker comes in a gorgeous Sage colorway and is now on sale for $30 off. Whether you prefer pods or grounds, this device makes tasty brews in 7 sizes (6-oz. to 24-oz) and has a built-in, fold-away milk frother!<a class="view-deal button" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LP9SF1?" target="_blank" rel="nofollow" data-dimension112="69b10030-a21f-11f1-87c3-c57969611a45" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="69b100a8-a21f-11f1-9e18-95eb917f62af" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109" href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="JkaewDaMJCoHarzBbjk5u8" name="Ninja BlendBOSS deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/JkaewDaMJCoHarzBbjk5u8.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow" data-dimension112="69b100a8-a21f-11f1-9e18-95eb917f62af" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109">BlendBOSS Blender: was $129 now $109</a></strong><br>The Ninja BlendBOSS is one of the best personal blenders we've used. In our <a href="https://www.tomsguide.com/home/home-appliances/ninja-blendboss-review">Ninja BlendBOSS review</a>, we said the Editor's Choice blender is great at everything from making hummus to lump-free smoothies. Its' compact, comes in a range of colors, and it's simply the best personal blender you can buy at this price point. <a class="view-deal button" href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow" data-dimension112="69b100a8-a21f-11f1-9e18-95eb917f62af" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109">View Deal</a></p></div><div class="product"><a data-dimension112="69b10120-a21f-11f1-8d7b-654bb46f6692" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129" href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:522px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="pKMqdL3azKMYSNwdTHk4m9" name="Ninja Never Clog" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/pKMqdL3azKMYSNwdTHk4m9.jpg" mos="" align="middle" fullscreen="" width="522" height="522" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow" data-dimension112="69b10120-a21f-11f1-8d7b-654bb46f6692" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129">NeverClog Cold Press Juicer: was $149 now $129</a></strong><br>Create healthy juices from the comforts of your home with the Ninja Cold Press Juicer Pro. It features a powerful high torque motor and includes two one-touch programs that make it easy to operate. We also like that it has a pulp control that allows you to customize your juice with pulp or without.<a class="view-deal button" href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow" data-dimension112="69b10120-a21f-11f1-8d7b-654bb46f6692" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="69b10198-a21f-11f1-8c60-5be5d32ce5bf" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$159" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1676px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="sWibiMn9jcqpLxyQRazy6f" name="Ninja Amazon 2.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sWibiMn9jcqpLxyQRazy6f.jpg" mos="" align="middle" fullscreen="" width="1676" height="1676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="69b10198-a21f-11f1-8c60-5be5d32ce5bf" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$159">Mega Kitchen System: was $219 now $159</a></strong><br>Leave all the multi-tasking in the kitchen to one appliance with this Ninja blender. With four functions for smoothies, processing, dough, drinks and more it will quickly become your go-to kitchen appliance. With this ultimate appliance, you’ll be enjoying fresh, healthy drinks, homemade bread and sweet bakes.<a class="view-deal button" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="69b10198-a21f-11f1-8c60-5be5d32ce5bf" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$159">View Deal</a></p></div><div class="product"><a data-dimension112="69b10206-a21f-11f1-b6d6-d311f0210fc6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:85.86%;"><img id="zNkXNCVYCubXhCRwjaoGPj" name="Ninjs Crispi" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zNkXNCVYCubXhCRwjaoGPj.jpg" mos="" align="middle" fullscreen="" width="679" height="583" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="69b10206-a21f-11f1-b6d6-d311f0210fc6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">Crispi 4-in-1 Portable Glass Air Fryer: was $179 now $159</a></strong><br>The Ninja Crispi is one of Ninja's most interesting recent releases. It features a removable cooking lid and interchangeable glass containers to cook meals of a range of sizes. In our <a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">Ninja Crispi review</a>, it earned a well-deserved 4.5 stars.  <a class="view-deal button" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="69b10206-a21f-11f1-b6d6-d311f0210fc6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">View Deal</a></p></div><div class="product"><a data-dimension112="69b1027e-a21f-11f1-b242-23fcfb084303" data-action="Deal Block" data-label="Double Oven, 12-in-1" data-dimension48="Double Oven, 12-in-1" data-dimension25="$219" href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:875px;"><p class="vanilla-image-block" style="padding-top:85.60%;"><img id="LQBDeKyzWKbLWRQgh54YmA" name="Double Oven, 12-in-1 DCT401" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/LQBDeKyzWKbLWRQgh54YmA.jpg" mos="" align="middle" fullscreen="" width="875" height="749" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow" data-dimension112="69b1027e-a21f-11f1-b242-23fcfb084303" data-action="Deal Block" data-label="Double Oven, 12-in-1" data-dimension48="Double Oven, 12-in-1" data-dimension25="$219">Double Oven, 12-in-1: was $379 now $219</a></strong><br>We couldn’t recommend this toaster oven more. Although it has since been replaced by a newer model, the DCT401 is a previous winner on our list of the <a href="https://www.tomsguide.com/best-picks/best-toaster-ovens">best toaster ovens</a>. We love that it divides into two ovens you can access separately via the FlexDoor, so you can cook two things at once. There are 12 functions overall, including toast, bake, whole roast, air fry, convection and pizza — meaning you can cook pretty much anything in there however you like.<a class="view-deal button" href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow" data-dimension112="69b1027e-a21f-11f1-b242-23fcfb084303" data-action="Deal Block" data-label="Double Oven, 12-in-1" data-dimension48="Double Oven, 12-in-1" data-dimension25="$219">View Deal</a></p></div><div class="product"><a data-dimension112="69b102ec-a21f-11f1-9afb-01ba23bf4691" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229" href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:75.80%;"><img id="sFc44K8P9eTgsrZ4Kc25oB" name="Foodi XL Pro Air Fry Oven, 10-in-1" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sFc44K8P9eTgsrZ4Kc25oB.jpg" mos="" align="middle" fullscreen="" width="1500" height="1137" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow" data-dimension112="69b102ec-a21f-11f1-9afb-01ba23bf4691" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229">Foodi XL Pro Air Fry Oven, 10-in-1: was $379 now $229</a></strong><br>40% off is one of the biggest Ninja discounts you'll find right now. While this XL air fryer/oven takes up a lot of space, it offers plenty of cooking functions and perfect cooking results every time. <a class="view-deal button" href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow" data-dimension112="69b102ec-a21f-11f1-9afb-01ba23bf4691" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229">View Deal</a></p></div><div class="product"><a data-dimension112="69b10364-a21f-11f1-85ce-a5e1ab8f06dc" data-action="Deal Block" data-label="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension48="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension25="$242" href="https://www.amazon.com/Ninja-Woodfire-Functionality-Authentic-OG301/dp/B0DZF2DV3T?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1429px;"><p class="vanilla-image-block" style="padding-top:104.97%;"><img id="zyLJdnsjZA39kWuyNGm55G" name="Woodire outdoor and grill" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zyLJdnsjZA39kWuyNGm55G.jpg" mos="" align="middle" fullscreen="" width="1429" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Woodfire-Functionality-Authentic-OG301/dp/B0DZF2DV3T?" target="_blank" rel="nofollow" data-dimension112="69b10364-a21f-11f1-85ce-a5e1ab8f06dc" data-action="Deal Block" data-label="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension48="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension25="$242">Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker: was $349 now $242</a></strong><br>You can score almost $100 off this Ninja outdoor grill and smoker right now. This smart grill lets you monitor cooking progress from your phone, which is great if you're juggling multiple dishes at once during a cookout! Be sure to apply the on-page coupon.<a class="view-deal button" href="https://www.amazon.com/Ninja-Woodfire-Functionality-Authentic-OG301/dp/B0DZF2DV3T?" target="_blank" rel="nofollow" data-dimension112="69b10364-a21f-11f1-85ce-a5e1ab8f06dc" data-action="Deal Block" data-label="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension48="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension25="$242">View Deal</a></p></div><div class="product"><a data-dimension112="69b103d2-a21f-11f1-9608-5fa98b37418f" data-action="Deal Block" data-label="Woodfire Oven & Smoker" data-dimension48="Woodfire Oven & Smoker" data-dimension25="$279" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:90.20%;"><img id="C5KWDYUzjspNSP6tT8AAQP" name="81IeXOFExqL._AC_SL1500_.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/C5KWDYUzjspNSP6tT8AAQP.jpg" mos="" align="middle" fullscreen="" width="1500" height="1353" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Woodfire Oven & Smoker is a favorite with the Tom's Guide team. It makes amazing pizzas, smoked wings and many things in between. It's perfect to keep the summer vibes going all year long with delicious dishes.<a class="view-deal button" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow" data-dimension112="69b103d2-a21f-11f1-9608-5fa98b37418f" data-action="Deal Block" data-label="Woodfire Oven & Smoker" data-dimension48="Woodfire Oven & Smoker" data-dimension25="$279">View Deal</a></p></div><div class="product"><a data-dimension112="69b1044a-a21f-11f1-8f9c-c9e6b35d6a60" data-action="Deal Block" data-label="Fireside360 Outdoor Heater and Firepit" data-dimension48="Fireside360 Outdoor Heater and Firepit" data-dimension25="$404" href="https://www.amazon.com/Ninja-Fireside360-Fire-Heater-Modes/dp/B0GWFP3T3N?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:889px;"><p class="vanilla-image-block" style="padding-top:168.73%;"><img id="hCnMFizeFWLfCRyTyyFyuc" name="Ninja fireside" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/hCnMFizeFWLfCRyTyyFyuc.jpg" mos="" align="middle" fullscreen="" width="889" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Fireside360-Fire-Heater-Modes/dp/B0GWFP3T3N?" target="_blank" rel="nofollow" data-dimension112="69b1044a-a21f-11f1-8f9c-c9e6b35d6a60" data-action="Deal Block" data-label="Fireside360 Outdoor Heater and Firepit" data-dimension48="Fireside360 Outdoor Heater and Firepit" data-dimension25="$404">Fireside360 Outdoor Heater and Firepit: was $499 now $404</a></strong><br>The newly-launched Fireside360 is a smokeless firepit and heater in one neat unit. There are three heat modes, you can feel the warmth up to 13ft. away, and since its powered by propane, there's nothing to clean away. Be sure to apply the on-page coupon to get this price.<a class="view-deal button" href="https://www.amazon.com/Ninja-Fireside360-Fire-Heater-Modes/dp/B0GWFP3T3N?" target="_blank" rel="nofollow" data-dimension112="69b1044a-a21f-11f1-8f9c-c9e6b35d6a60" data-action="Deal Block" data-label="Fireside360 Outdoor Heater and Firepit" data-dimension48="Fireside360 Outdoor Heater and Firepit" data-dimension25="$404">View Deal</a></p></div><div class="product"><a data-dimension112="69b104c2-a21f-11f1-a622-75b3da269b63" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599" href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:541px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="HcnQ36qmUJg5VJidxsJ76X" name="Ninja Luxe Cafe.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/HcnQ36qmUJg5VJidxsJ76X.jpg" mos="" align="middle" fullscreen="" width="541" height="541" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>It's not on sale, but the Ninja Luxe Café Premier is just that good. This 3-in-1 coffee machine brews quality espresso, drip coffee, and fast cold brew using its Barista Assist technology, an integrated grinder, and weight-based dosing. Plus, it makes creating pro-level microfoam easy, thanks to its Dual Froth system. Our reviewer called it "the best beginner-friendly espresso machine I’ve ever tested" in our <a href="https://www.tomsguide.com/home/ninja-luxe-cafe-premier-series-espresso-machine-review" data-dimension112="69b104c2-a21f-11f1-a622-75b3da269b63" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599">Ninja Luxe Café coffee maker review</a>.<a class="view-deal button" href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow" data-dimension112="69b104c2-a21f-11f1-a622-75b3da269b63" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599">View Deal</a></p></div><div class="product"><a data-dimension112="69b10530-a21f-11f1-8a88-1d69015b22f5" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999" href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0GJ76517J" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1490px;"><p class="vanilla-image-block" style="padding-top:100.67%;"><img id="anfQB46xz27Fy6KWn9ysqX" name="Ninja Flexflame" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/anfQB46xz27Fy6KWn9ysqX.jpg" mos="" align="middle" fullscreen="" width="1490" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0GJ76517J" target="_blank" rel="nofollow" data-dimension112="69b10530-a21f-11f1-8a88-1d69015b22f5" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999">FlexFlame Propane Grill: was $1119 now $999</a></strong><br>The Ninja FlexFlame is Ninja's first attempt at a propane grill, and it offers all the ingeniuity you'd expect from a Ninja product. It's got a pellet chamber to infuse your food with an authentic smoked flavor, but can also sear and even be converted to a griddle with the use of add-ons.<a class="view-deal button" href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0GJ76517J" target="_blank" rel="nofollow" data-dimension112="69b10530-a21f-11f1-8a88-1d69015b22f5" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999">View Deal</a></p></div><h3 class="article-body__section" id="section-more-deals"><span>More Deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Home+Appliances&brands=Ninja%2CShark&min_discount_ratio=0.95&offer_type=all&view_mode=carousel&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=light_blue" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/LCxBaXIgdGFncywsLCwsLCwsLCwsLCwsDQosLFBvcnRhYmxlIGNoYXJnZXJzL3dpcmVsZXNzIGNoYXJnZXJzLCwsLCwsLCwsLCwsLCwNCiwsIldhdGVyIGJvdHRsZXMgKHN0YW5sZXlzLCBPd2FsYSwgSHlkcm8gZmxhc2ssIFlldGkpIiwsLCwsLCwsLCwsLCwsDQosLEhhbmQgaGVsZCBmYW5zLCwsLCwsLCwsLCwsLCwNCiwsLCwsLCwsLCwsLCwsLCwNCmhvbWUgb2ZmaWNlLCxvZmZpY2UgY2hhaXJzLCwsLCwsLCwsLCwsLCwNCiwsc3RhbmRpbmcgZGVza3MsLCwsLCwsLCwsLCwsLA0KLCxtb25pdG9ycywsLCwsLCwsLCwsLCwsDQosLEtleWJvYXJkcywsLCwsLCwsLCwsLCwsDQosLGRvY2tpbmcgc3RhdGlvbiwsLCwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpHYW1pbmcsLENvbnNvbGVzLCwsLCwsLCwsLCwsLCwNCiwsQWNjZXNzb3JpZXMsLCwsLCwsLCwsLCwsLA0KLCxHYW1lcywsLCwsLCwsLCwsLCwsDQosLENvdWxkIGluY2x1ZGUgTGVnbz8sLCwsLCwsLCwsLCwsLA==")));              const text = preloadedCSV;              const parsed = parseCSV(text);                            const rowsByName = {};              let filterStart = -1;              parsed.forEach((rc, i) => {                 if (rc && rc.length > 0 && rc[0]) rowsByName[rc[0]] = rc;                 if (rc && rc.length > 0 && rc[0] === 'Filter buttons') filterStart = i;              });                            const cols = [];              if(rowsByName['Roundel text']) {                const headerRow = rowsByName['Roundel text'];                for(let col = 1; col < headerRow.length; col++) {                   let label = headerRow[col];                   if (!label) continue;                                      let q = rowsByName['Search Query'] && rowsByName['Search Query'][col] ? rowsByName['Search Query'][col] : '';                   let img = rowsByName['Roundel image'] && rowsByName['Roundel image'][col] ? rowsByName['Roundel image'][col] : '';                   let ds = rowsByName['Discount Amount'] && rowsByName['Discount Amount'][col] ? rowsByName['Discount Amount'][col] : '';                   let pr = rowsByName['Price Range'] && rowsByName['Price Range'][col] ? rowsByName['Price Range'][col] : '';                   let rt = rowsByName['Retailer'] && rowsByName['Retailer'][col] ? rowsByName['Retailer'][col] : '';                   let ot = rowsByName['Offer Type'] && rowsByName['Offer Type'][col] ? rowsByName['Offer Type'][col] : '';                                      let filters = [];                   if(filterStart > 0) {                     for(let r = filterStart + 1; r < parsed.length; r++) {                         if(!parsed[r] || parsed[r][0] === 'Notes' || parsed[r][0] === 'Categories to consider') break;                         let f = parsed[r][col];                         if(f) filters.push(f);                     }                   }                   cols.push({ label, img, q, ds, pr, rt, ot, filters });                }              }              this.carouselData = cols;              if (this.carouselData && this.carouselData.length > 0) {                 const isMatched = this.carouselData.some(c => c.q === this.currentQuery || c.label === this.currentQuery);                 if (!isMatched) {                    const first = this.carouselData[0];                    this.currentQuery = first.q || first.label;                    if (this.priceFilter) this.priceFilter.value = 'all';                    if (this.customPriceMin) this.customPriceMin.value = '';                    if (this.customPriceMax) this.customPriceMax.value = '';                    let dPr = first.pr || 'all';                    if (typeof dPr === 'string' && dPr !== 'all') {                       let prLower = dPr.toLowerCase();                       if (prLower.includes('min') || prLower.includes('over')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else if (prLower.includes('max') || prLower.includes('under')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       }                    }                    let dAm = '0';                    if(first.ds && typeof first.ds === 'string') {                       let m = first.ds.match(/(\d+)/);                       if(m) dAm = m[1];                    }                    if (this.discountFilter) this.discountFilter.value = dAm;                    if (this.offerTypeSelect) this.offerTypeSelect.value = first.ot || '';                    if (this.retailerSelect) this.retailerSelect.value = first.rt || '';                    this.selectedBrands = [];                    if (this.brandDropdown) {                        const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                        chks.forEach(chk => chk.checked = false);                    }                    if (this.searchInput) this.searchInput.value = this.currentQuery;                 }              }              this.renderCarouselUI();          } catch(e){ console.warn(e); }        }                renderCarouselUI() {           const roundelWrapper = this.root.querySelector('.tg-df-carousel-roundels');           if(!roundelWrapper || !this.carouselData) return;                      let html = '';           this.carouselData.forEach(r => {              const q = r.q || r.label;              const isActive = (this.currentQuery === q || this.currentQuery === r.label) ? 'active' : '';              const imgHtml = r.img ? `\x3Cimg src="${r.img}" alt="${r.label}" />` : `\x3Csvg width="32" height="32" fill="#1F69FF" viewBox="0 0 24 24">\x3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\x3C/svg>`;              html += `                \x3Cdiv class="tg-df-roundel tg-df-carousel-cat ${isActive}" data-label="${this.escapeHTML(r.label)}">                  \x3Cdiv class="tg-df-roundel-img-box">${imgHtml}\x3C/div>                  \x3Cspan class="tg-df-roundel-label">${this.escapeHTML(r.label)}\x3C/span>                \x3C/div>              `;           });           roundelWrapper.innerHTML = html;                      // Rebind clicks           const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');           roundels.forEach(rNode => {             rNode.addEventListener('click', () => {                const r = this.carouselData.find(c => c.label === rNode.getAttribute('data-label'));                 if(!r) return;                                  if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: r.label,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = r.q || r.label;                const labelTitle = this.root.querySelector('#tg-df-carousel-title-label');                if (labelTitle) labelTitle.textContent = 'Best ' + this.currentQuery;                if (this.priceFilter) this.priceFilter.value = 'all';                if (this.customPriceMin) this.customPriceMin.value = '';                if (this.customPriceMax) this.customPriceMax.value = '';                let dPr = r.pr || 'all';                if (typeof dPr === 'string' && dPr !== 'all') {                   let prLower = dPr.toLowerCase();                   if (prLower.includes('min') || prLower.includes('over')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMin) this.customPriceMin.value = m[1];                   } else if (prLower.includes('max') || prLower.includes('under')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMax) this.customPriceMax.value = m[1];                   }                }                                let discountAmount = '0';                if(r.ds && typeof r.ds === 'string') {                   let m = r.ds.match(/(\d+)/);                   if(m) discountAmount = m[1];                }                if (this.discountFilter) this.discountFilter.value = discountAmount;                if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                                // Clear brands                    this.selectedBrands = [];                    if (this.brandDropdown) {                    const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                    chks.forEach(chk => chk.checked = false);                }                                if (this.searchInput) this.searchInput.value = this.currentQuery;                                roundels.forEach(ro => ro.classList.remove('active'));                if (rNode) rNode.classList.add('active');                                this.renderCarouselFilters(r);                this.fetchDeals(this.currentQuery);             });           });                      // Auto-highlight active           const activeR = this.carouselData.find(c => c.q === this.currentQuery || c.label === this.currentQuery);           if(activeR) this.renderCarouselFilters(activeR);        }                renderCarouselFilters(r) {           const filtersWrap = this.root.querySelector('.tg-df-carousel-filters-wrap');           if(!filtersWrap) return;                      let html = `\x3Cbutton class="tg-df-carousel-filter-btn" data-type="all">All\x3C/button>`;                      r.filters.forEach(f => {              let fL = f.toLowerCase();              let icon = '';              let logic = `data-type="custom" data-v="${this.escapeHTML(f)}"`;              if (fL === 'amazon deals' || fL === 'prime deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>`;              } else if (fL === 'lightning deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>`;              } else {                 if (fL.includes('lightning')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap">\x3Cpolygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2">\x3C/polygon>\x3C/svg>`;                 } else if (fL.includes('% off')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>`;                 } else if (fL.includes('under') || fL.includes('min ')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>`;                 }                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>${icon} ${this.escapeHTML(f)}\x3C/button>`;              }           });                      filtersWrap.innerHTML = html;                      const btns = filtersWrap.querySelectorAll('button');           btns.forEach(b => {             b.addEventListener('click', () => {                const type = b.getAttribute('data-type');                if (type === 'custom') {                   const v = b.getAttribute('data-v');                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-custom-${(v||'').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`, name: 'Custom Filter', label: v });                }                if (type === 'all') {                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: 'filter-clear-all', name: 'Clear all', label: 'Clear all filters' });                   // reset everything                   btns.forEach(btn => btn.classList.remove('active'));                   b.classList.add('active');                                      // Reset prices                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   if (this.brandDropdown) {                     const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                     chks.forEach(chk => chk.checked = false);                   }                } else {                   const v = b.getAttribute('data-v');                   const fL = v.toLowerCase();                                      let mapRet = ['amazon', 'walmart', 'best buy', 'target', 'john lewis', 'currys', 'argos'];                   const getCategory = (s) => {                      if (s === 'lightning deals' || s === 'amazon deals' || s === 'prime deals') return 'offer';                      if (s.includes('% off')) return 'discount';                      if (s.includes('under') || s.includes('over') || s.includes('min') || s.includes('max')) return 'price';                      if (mapRet.includes(s)) return 'retailer';                      return 'brand';                   };                   const cat = getCategory(fL);                   const wasActive = b.classList.contains('active');                   if (cat !== 'brand') {                      btns.forEach(btn => {                          if (btn === b) return;                          if (btn.getAttribute('data-type') === 'all') return;                          const bV = btn.getAttribute('data-v');                          if (!bV) return;                          if (getCategory(bV.toLowerCase()) === cat) btn.classList.remove('active');                      });                   }                   if (wasActive) b.classList.remove('active');                   else b.classList.add('active');                   let anyActive = Array.from(btns).some(btn => btn !== btns[0] && btn.classList.contains('active'));                   if (!anyActive) {                       btns[0].click();                       return;                   } else {                       btns[0].classList.remove('active');                   }                                      if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   btns.forEach(btn => {                       if (!btn.classList.contains('active') || btn.getAttribute('data-type') === 'all') return;                       const vv = btn.getAttribute('data-v');                       const vl = vv.toLowerCase();                                              if (vl === 'lightning deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_lightning';                       } else if (vl === 'amazon deals' || vl === 'prime deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_prime';                       } else if (vl.includes('% off')) {                          let m = vl.match(/(\d+)%/);                          if (m && this.discountFilter) this.discountFilter.value = m[1];                       } else if (vl.includes('under') || vl.includes('max')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       } else if (vl.includes('min') || vl.includes('over')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else {                          let foundR = mapRet.find(x => x === vl);                          if (foundR) {                             let realR = ['Amazon', 'Walmart', 'Best Buy', 'Target', 'John Lewis', 'Currys', 'Argos'].find(x => x.toLowerCase() === vl);                             if (this.retailerSelect) this.retailerSelect.value = realR;                          } else {                             this.selectedBrands.push(vv);                          }                       }                   });                                      if (this.brandDropdown) {                       const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                       chks.forEach(c => c.checked = this.selectedBrands.includes(c.value));                   }                                      if (r.pr && typeof r.pr === 'string') {                       let prL = r.pr.toLowerCase();                       if (prL.includes('under $')) {                           let m = prL.match(/under \$(\d+)/i);                           if (m && this.customPriceMax && !this.customPriceMax.value) this.customPriceMax.value = m[1];                       }                   }                }                                this.fetchDeals(this.currentQuery);             });           });                      // default to highlighting first           btns[0].classList.add('active');        }async fetchDeals(query, append = false) {          if (!append) {             this.showLoading();             this.deals = [];             this.displayLimit = this.getRowsLimit();          } else {             this.displayLimit += this.getRowsLimit();          }                    try {            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad(append);            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query, append);               } else {                 await this.fetchHawkDeals(query, append);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query, append);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const url = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let dynamicBrandsCounts = {};                    targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                   dynamicBrandsCounts[data.brand] = (dynamicBrandsCounts[data.brand] || 0) + 1;                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || ''                });             });          });                    const airedaleBrandsList = Object.keys(dynamicBrandsCounts).map(b => ({              formatted_value: b,              count: dynamicBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query, append = false) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (append && this.deals.length > 0) {            url.searchParams.append('offset', this.deals.length.toString());          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : (this.getViewMode() === 'savings_squad' ? 'date_desc' : 'discount_desc');          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin-top:8px;">${_div}                ${_div}              ${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)} (${this.airedaleTagCounts[tag] || 0})${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = isSavingsSquadMode ? 'View Deal' : 'Check Price';              if (isSavingsSquadMode) {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                  ${_div}                `;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = `                \x3Cdiv class="tg-df-card-merchant-wrapper">                  \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                ${_div}                \x3Cdiv class="tg-df-card-price-group">                  ${isSavingsSquadMode ? '' : `                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                  `}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description">\x3Cp>${this.escapeHTML(deal.description)}${_p}${_div}` : ''}                      ${_div}                    ${_div}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>${deal.isCheckPrice ? 'Check Price' : 'View Deal'}${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cp style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 12px; line-height: 1.4;">${this.escapeHTML(deal.description)}${_p}` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${priceGroupHtml}                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta ${isSavingsSquadMode ? 'tg-df-cta-savings-squad' : ''}" style="text-decoration: none;">${ctaText}${_a}                  ${_div}                ${_div}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit || ((this.getViewMode() === 'carousel' || this.getViewMode() === 'auto') && displayDeals.length > 0 && displayDeals.length % ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12) === 0)) {            if (this.getViewMode() === 'carousel') {               dealsHtml += `                 \x3Cbutton type="button" class="tg-df-load-more-card tg-df-load-more">                   \x3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;">\x3Cpath d="M5 12h14">\x3C/path>\x3Cpath d="m12 5 7 7-7 7">\x3C/path>\x3C/svg>                   Load More                 ${_button}               `;            } else {               dealsHtml += `                 \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                   \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer; display: flex; align-items: center;">Load More${_button}                 ${_div}               `;            }          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let rightChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             let leftChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-left');             if (this.getViewMode() === 'carousel') {                 // The observer set up in setupScrollListeners handles visibility.                 if (rightChevron) rightChevron.style.display = 'flex';                 if (leftChevron) leftChevron.style.display = 'none'; // reset correctly             } else {                 if (rightChevron) rightChevron.style.display = 'none';                 if (leftChevron) leftChevron.style.display = 'none';             }          }                    const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', async () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              if (displayDeals.length <= this.displayLimit) {                 loadMoreBtn.innerHTML = `                  <svg class="tg-df-spinner" style="width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 8px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/></svg>                  Loading...                 `;                 loadMoreBtn.disabled = true;                 await this.fetchDeals(this.currentQuery, true);              } else {                 this.displayLimit += this.getRowsLimit();                 this.render();              }            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            setTimeout(() => {               if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');               document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));            }, 50);          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/these-15-ninja-deals-are-too-good-to-miss-ahead-of-labor-day-save-up-to-40-percent-on-blenders-air-fryers-and-more</link>
                                                                            <description>
                            <![CDATA[ A bunch of Ninja appliances are on sale at Amazon — check out the biggest upgrades for your home and kitchen. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">sfpKEdWYWGXpHxDyudu2pL</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/RSq6p7Tgy8whV7xn5Ruf6J-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 27 Aug 2026 14:32:10 +0000</pubDate>                                                                                                                                <updated>Fri, 04 Sep 2026 11:52:43 +0000</updated>
                                                                                                                                            <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                                    <dc:creator><![CDATA[ Olivia Halevy ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/YAg7Fmh9dGKbg3hULBMWiU.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Olivia is a Deals Writer at Tom’s Guide. She loves all things lifestyle, shopping and sourcing the best deals from some of the top brands, retailers and websites around. From fashion and beauty to smart home and tech, she has an eye for finding practical, stylish, and reasonably-priced items that help make life simpler (and a little more aesthetically-pleasing).&amp;nbsp;Olivia has over five years of experience writing lifestyle, commerce, and branded content for &lt;a href=&quot;http://patch.com/&quot;&gt;Patch.com&lt;/a&gt;. She&#039;s covered major shopping holidays like Amazon Prime Day, Black Friday and Cyber Monday. In her free time, she enjoys trying out new exercise classes, binge-watching the latest viral TV shows, cooking, spending time with family and taking long walks with her dog, Gus.&amp;nbsp;&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/RSq6p7Tgy8whV7xn5Ruf6J-1280-80.jpg">
                                                            <media:credit><![CDATA[Amazon]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Ninja deals]]></media:description>                                                            <media:text><![CDATA[Ninja deals]]></media:text>
                                <media:title type="plain"><![CDATA[Ninja deals]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/RSq6p7Tgy8whV7xn5Ruf6J-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Labor Day is almost here — and if you're hosting any backyard barbecues or celebrations, chances are you could use a little help. That's where Ninja comes into play! The kitchen appliance brand has everything you need to meal prep and get grilling! </p><p>Fortunately, <a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow">Amazon is closing out the summer with a bunch of Ninja appliance deals</a>. From high-quality blenders and air fryers to pizza ovens and grills, there are some discounts here that you definitely don't want to miss — especially since the best deals are currently up to 40% off.</p><p>I've been scouring the sale and have found the best cooking deals to help you in the kitchen this Labor Day and beyond. I've listed all my favorites below, so keep scrolling to check them out.</p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow"><strong>shop all Ninja deals at Amazon</strong></a></li><li><strong>Ninja Blast Max: </strong><a href="https://www.amazon.com/dp/B0DB964918?" target="_blank" rel="nofollow"><strong>was $99 now $69</strong></a></li><li><strong>Ninja Iced Coffee Maker: </strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LNKN4Z?" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Ninja Crispi 4-in-1 Portable Glass Air Fryer: </strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><strong>was $179 now $159</strong></a></li><li><strong>Ninja Double Oven, 12-in-1: </strong><a href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow"><strong>was $379 now $219</strong></a></li><li><strong>Ninja Woodfire Oven & Smoker: </strong><a href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow"><strong>was $399 now $279</strong></a></li><li><strong>Ninja Luxe Café Premier: </strong><a href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow"><strong>now $599</strong></a></li><li><strong>Ninja FlexFlame Propane Grill: </strong><a href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0GJ76517J" target="_blank" rel="nofollow"><strong>was $1,119 now $999</strong></a></li></ul><h3 class="article-body__section" id="section-ninja-deals"><span>Ninja Deals</span></h3><div class="product"><a data-dimension112="69b0fedc-a21f-11f1-8a6e-418eb9d3d485" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69" href="https://www.amazon.com/dp/B0DB964918?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1216px;"><p class="vanilla-image-block" style="padding-top:123.36%;"><img id="9Q4QY399FaozAFUt4j8dTW" name="ninja-blast-max-portable-blender--twist--bb517479-54ce-4a77-974c-58e2f44dde33.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/9Q4QY399FaozAFUt4j8dTW.jpg" mos="" align="middle" fullscreen="" width="1216" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Blast Max is capable of taking on any challenge you throw at it, including crushing ice and blitzing berries to make yummy smoothies. It comes with multiple blend settings to make sure your blender is using the right level of power for the task at hand, and the compact size means it’s a great option for anyone with a small kitchen. <a class="view-deal button" href="https://www.amazon.com/dp/B0DB964918?" target="_blank" rel="nofollow" data-dimension112="69b0fedc-a21f-11f1-8a6e-418eb9d3d485" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69">View Deal</a></p></div><div class="product"><a data-dimension112="69b0ff4a-a21f-11f1-9b87-d7697e968c6e" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$79" href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B0GH8PVFHS?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:162.30%;"><img id="sFftzctqbej7oaSQLEtBRj" name="Ninja Pro Blender" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sFftzctqbej7oaSQLEtBRj.jpg" mos="" align="middle" fullscreen="" width="679" height="1102" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B0GH8PVFHS?" target="_blank" rel="nofollow" data-dimension112="69b0ff4a-a21f-11f1-9b87-d7697e968c6e" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$79">Professional Blender: was $109 now $79</a></strong><br>Be it daily smoothies or making ice cream, this blender can tackle it all, at the touch of a button. Also featured on our <a href="https://www.tomsguide.com/best-picks/best-blenders">best blenders</a> guide, this comes with 3 preset Auto-iQ programs that creates smoothies, frozen drinks, and ice cream, and its powerful blades can crush ice in no time. This is a great deal if you want an easy-to-use blender, with professional results.  <a class="view-deal button" href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B0GH8PVFHS?" target="_blank" rel="nofollow" data-dimension112="69b0ff4a-a21f-11f1-9b87-d7697e968c6e" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$79">View Deal</a></p></div><div class="product"><a data-dimension112="69b0ffc2-a21f-11f1-b219-a33e5661b9bb" data-action="Deal Block" data-label="Air Fryer Pro 4-in-1" data-dimension48="Air Fryer Pro 4-in-1" data-dimension25="$89" href="https://www.amazon.com/dp/B0CSZ7WBYW" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:560px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="f49Q8qGkV5TG2EsuDo3or5" name="Ninja Air Fryer Pro 4 in 1" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/f49Q8qGkV5TG2EsuDo3or5.jpg" mos="" align="middle" fullscreen="" width="560" height="560" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Air Fryer Pro 4-in-1 is a versatile kitchen appliance that air fries, roasts, reheats, and dehydrates. With its 5-quart capacity, it cooks food quickly using hot air circulation, delivering crispy, healthier meals with little to no oil. That makes it <em>perfect </em>for everyday cooking and batch meal prep!<a class="view-deal button" href="https://www.amazon.com/dp/B0CSZ7WBYW" target="_blank" rel="nofollow" data-dimension112="69b0ffc2-a21f-11f1-b219-a33e5661b9bb" data-action="Deal Block" data-label="Air Fryer Pro 4-in-1" data-dimension48="Air Fryer Pro 4-in-1" data-dimension25="$89">View Deal</a></p></div><div class="product"><a data-dimension112="69b10030-a21f-11f1-87c3-c57969611a45" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LP9SF1?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:99.56%;"><img id="4uiaRXRKY8Go6F4ZpM7UdA" name="Ninja coffee" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4uiaRXRKY8Go6F4ZpM7UdA.jpg" mos="" align="middle" fullscreen="" width="679" height="676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LP9SF1?" target="_blank" rel="nofollow" data-dimension112="69b10030-a21f-11f1-87c3-c57969611a45" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">Pods & Grounds Specialty Single-Serve Iced Coffee Maker: was $129 now $99</a></strong><br>This stylish Ninja coffee maker comes in a gorgeous Sage colorway and is now on sale for $30 off. Whether you prefer pods or grounds, this device makes tasty brews in 7 sizes (6-oz. to 24-oz) and has a built-in, fold-away milk frother!<a class="view-deal button" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LP9SF1?" target="_blank" rel="nofollow" data-dimension112="69b10030-a21f-11f1-87c3-c57969611a45" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="69b100a8-a21f-11f1-9e18-95eb917f62af" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109" href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="JkaewDaMJCoHarzBbjk5u8" name="Ninja BlendBOSS deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/JkaewDaMJCoHarzBbjk5u8.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow" data-dimension112="69b100a8-a21f-11f1-9e18-95eb917f62af" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109">BlendBOSS Blender: was $129 now $109</a></strong><br>The Ninja BlendBOSS is one of the best personal blenders we've used. In our <a href="https://www.tomsguide.com/home/home-appliances/ninja-blendboss-review">Ninja BlendBOSS review</a>, we said the Editor's Choice blender is great at everything from making hummus to lump-free smoothies. Its' compact, comes in a range of colors, and it's simply the best personal blender you can buy at this price point. <a class="view-deal button" href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow" data-dimension112="69b100a8-a21f-11f1-9e18-95eb917f62af" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109">View Deal</a></p></div><div class="product"><a data-dimension112="69b10120-a21f-11f1-8d7b-654bb46f6692" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129" href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:522px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="pKMqdL3azKMYSNwdTHk4m9" name="Ninja Never Clog" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/pKMqdL3azKMYSNwdTHk4m9.jpg" mos="" align="middle" fullscreen="" width="522" height="522" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow" data-dimension112="69b10120-a21f-11f1-8d7b-654bb46f6692" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129">NeverClog Cold Press Juicer: was $149 now $129</a></strong><br>Create healthy juices from the comforts of your home with the Ninja Cold Press Juicer Pro. It features a powerful high torque motor and includes two one-touch programs that make it easy to operate. We also like that it has a pulp control that allows you to customize your juice with pulp or without.<a class="view-deal button" href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow" data-dimension112="69b10120-a21f-11f1-8d7b-654bb46f6692" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="69b10198-a21f-11f1-8c60-5be5d32ce5bf" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$159" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1676px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="sWibiMn9jcqpLxyQRazy6f" name="Ninja Amazon 2.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sWibiMn9jcqpLxyQRazy6f.jpg" mos="" align="middle" fullscreen="" width="1676" height="1676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="69b10198-a21f-11f1-8c60-5be5d32ce5bf" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$159">Mega Kitchen System: was $219 now $159</a></strong><br>Leave all the multi-tasking in the kitchen to one appliance with this Ninja blender. With four functions for smoothies, processing, dough, drinks and more it will quickly become your go-to kitchen appliance. With this ultimate appliance, you’ll be enjoying fresh, healthy drinks, homemade bread and sweet bakes.<a class="view-deal button" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="69b10198-a21f-11f1-8c60-5be5d32ce5bf" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$159">View Deal</a></p></div><div class="product"><a data-dimension112="69b10206-a21f-11f1-b6d6-d311f0210fc6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:85.86%;"><img id="zNkXNCVYCubXhCRwjaoGPj" name="Ninjs Crispi" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zNkXNCVYCubXhCRwjaoGPj.jpg" mos="" align="middle" fullscreen="" width="679" height="583" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="69b10206-a21f-11f1-b6d6-d311f0210fc6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">Crispi 4-in-1 Portable Glass Air Fryer: was $179 now $159</a></strong><br>The Ninja Crispi is one of Ninja's most interesting recent releases. It features a removable cooking lid and interchangeable glass containers to cook meals of a range of sizes. In our <a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">Ninja Crispi review</a>, it earned a well-deserved 4.5 stars.  <a class="view-deal button" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="69b10206-a21f-11f1-b6d6-d311f0210fc6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">View Deal</a></p></div><div class="product"><a data-dimension112="69b1027e-a21f-11f1-b242-23fcfb084303" data-action="Deal Block" data-label="Double Oven, 12-in-1" data-dimension48="Double Oven, 12-in-1" data-dimension25="$219" href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:875px;"><p class="vanilla-image-block" style="padding-top:85.60%;"><img id="LQBDeKyzWKbLWRQgh54YmA" name="Double Oven, 12-in-1 DCT401" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/LQBDeKyzWKbLWRQgh54YmA.jpg" mos="" align="middle" fullscreen="" width="875" height="749" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow" data-dimension112="69b1027e-a21f-11f1-b242-23fcfb084303" data-action="Deal Block" data-label="Double Oven, 12-in-1" data-dimension48="Double Oven, 12-in-1" data-dimension25="$219">Double Oven, 12-in-1: was $379 now $219</a></strong><br>We couldn’t recommend this toaster oven more. Although it has since been replaced by a newer model, the DCT401 is a previous winner on our list of the <a href="https://www.tomsguide.com/best-picks/best-toaster-ovens">best toaster ovens</a>. We love that it divides into two ovens you can access separately via the FlexDoor, so you can cook two things at once. There are 12 functions overall, including toast, bake, whole roast, air fry, convection and pizza — meaning you can cook pretty much anything in there however you like.<a class="view-deal button" href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow" data-dimension112="69b1027e-a21f-11f1-b242-23fcfb084303" data-action="Deal Block" data-label="Double Oven, 12-in-1" data-dimension48="Double Oven, 12-in-1" data-dimension25="$219">View Deal</a></p></div><div class="product"><a data-dimension112="69b102ec-a21f-11f1-9afb-01ba23bf4691" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229" href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:75.80%;"><img id="sFc44K8P9eTgsrZ4Kc25oB" name="Foodi XL Pro Air Fry Oven, 10-in-1" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sFc44K8P9eTgsrZ4Kc25oB.jpg" mos="" align="middle" fullscreen="" width="1500" height="1137" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow" data-dimension112="69b102ec-a21f-11f1-9afb-01ba23bf4691" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229">Foodi XL Pro Air Fry Oven, 10-in-1: was $379 now $229</a></strong><br>40% off is one of the biggest Ninja discounts you'll find right now. While this XL air fryer/oven takes up a lot of space, it offers plenty of cooking functions and perfect cooking results every time. <a class="view-deal button" href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow" data-dimension112="69b102ec-a21f-11f1-9afb-01ba23bf4691" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229">View Deal</a></p></div><div class="product"><a data-dimension112="69b10364-a21f-11f1-85ce-a5e1ab8f06dc" data-action="Deal Block" data-label="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension48="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension25="$242" href="https://www.amazon.com/Ninja-Woodfire-Functionality-Authentic-OG301/dp/B0DZF2DV3T?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1429px;"><p class="vanilla-image-block" style="padding-top:104.97%;"><img id="zyLJdnsjZA39kWuyNGm55G" name="Woodire outdoor and grill" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zyLJdnsjZA39kWuyNGm55G.jpg" mos="" align="middle" fullscreen="" width="1429" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Woodfire-Functionality-Authentic-OG301/dp/B0DZF2DV3T?" target="_blank" rel="nofollow" data-dimension112="69b10364-a21f-11f1-85ce-a5e1ab8f06dc" data-action="Deal Block" data-label="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension48="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension25="$242">Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker: was $349 now $242</a></strong><br>You can score almost $100 off this Ninja outdoor grill and smoker right now. This smart grill lets you monitor cooking progress from your phone, which is great if you're juggling multiple dishes at once during a cookout! Be sure to apply the on-page coupon.<a class="view-deal button" href="https://www.amazon.com/Ninja-Woodfire-Functionality-Authentic-OG301/dp/B0DZF2DV3T?" target="_blank" rel="nofollow" data-dimension112="69b10364-a21f-11f1-85ce-a5e1ab8f06dc" data-action="Deal Block" data-label="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension48="Woodfire ProConnect Premium XL Outdoor 7-in-1 Grill & Smoker" data-dimension25="$242">View Deal</a></p></div><div class="product"><a data-dimension112="69b103d2-a21f-11f1-9608-5fa98b37418f" data-action="Deal Block" data-label="Woodfire Oven & Smoker" data-dimension48="Woodfire Oven & Smoker" data-dimension25="$279" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:90.20%;"><img id="C5KWDYUzjspNSP6tT8AAQP" name="81IeXOFExqL._AC_SL1500_.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/C5KWDYUzjspNSP6tT8AAQP.jpg" mos="" align="middle" fullscreen="" width="1500" height="1353" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Woodfire Oven & Smoker is a favorite with the Tom's Guide team. It makes amazing pizzas, smoked wings and many things in between. It's perfect to keep the summer vibes going all year long with delicious dishes.<a class="view-deal button" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow" data-dimension112="69b103d2-a21f-11f1-9608-5fa98b37418f" data-action="Deal Block" data-label="Woodfire Oven & Smoker" data-dimension48="Woodfire Oven & Smoker" data-dimension25="$279">View Deal</a></p></div><div class="product"><a data-dimension112="69b1044a-a21f-11f1-8f9c-c9e6b35d6a60" data-action="Deal Block" data-label="Fireside360 Outdoor Heater and Firepit" data-dimension48="Fireside360 Outdoor Heater and Firepit" data-dimension25="$404" href="https://www.amazon.com/Ninja-Fireside360-Fire-Heater-Modes/dp/B0GWFP3T3N?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:889px;"><p class="vanilla-image-block" style="padding-top:168.73%;"><img id="hCnMFizeFWLfCRyTyyFyuc" name="Ninja fireside" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/hCnMFizeFWLfCRyTyyFyuc.jpg" mos="" align="middle" fullscreen="" width="889" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Fireside360-Fire-Heater-Modes/dp/B0GWFP3T3N?" target="_blank" rel="nofollow" data-dimension112="69b1044a-a21f-11f1-8f9c-c9e6b35d6a60" data-action="Deal Block" data-label="Fireside360 Outdoor Heater and Firepit" data-dimension48="Fireside360 Outdoor Heater and Firepit" data-dimension25="$404">Fireside360 Outdoor Heater and Firepit: was $499 now $404</a></strong><br>The newly-launched Fireside360 is a smokeless firepit and heater in one neat unit. There are three heat modes, you can feel the warmth up to 13ft. away, and since its powered by propane, there's nothing to clean away. Be sure to apply the on-page coupon to get this price.<a class="view-deal button" href="https://www.amazon.com/Ninja-Fireside360-Fire-Heater-Modes/dp/B0GWFP3T3N?" target="_blank" rel="nofollow" data-dimension112="69b1044a-a21f-11f1-8f9c-c9e6b35d6a60" data-action="Deal Block" data-label="Fireside360 Outdoor Heater and Firepit" data-dimension48="Fireside360 Outdoor Heater and Firepit" data-dimension25="$404">View Deal</a></p></div><div class="product"><a data-dimension112="69b104c2-a21f-11f1-a622-75b3da269b63" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599" href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:541px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="HcnQ36qmUJg5VJidxsJ76X" name="Ninja Luxe Cafe.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/HcnQ36qmUJg5VJidxsJ76X.jpg" mos="" align="middle" fullscreen="" width="541" height="541" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>It's not on sale, but the Ninja Luxe Café Premier is just that good. This 3-in-1 coffee machine brews quality espresso, drip coffee, and fast cold brew using its Barista Assist technology, an integrated grinder, and weight-based dosing. Plus, it makes creating pro-level microfoam easy, thanks to its Dual Froth system. Our reviewer called it "the best beginner-friendly espresso machine I’ve ever tested" in our <a href="https://www.tomsguide.com/home/ninja-luxe-cafe-premier-series-espresso-machine-review" data-dimension112="69b104c2-a21f-11f1-a622-75b3da269b63" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599">Ninja Luxe Café coffee maker review</a>.<a class="view-deal button" href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow" data-dimension112="69b104c2-a21f-11f1-a622-75b3da269b63" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599">View Deal</a></p></div><div class="product"><a data-dimension112="69b10530-a21f-11f1-8a88-1d69015b22f5" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999" href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0GJ76517J" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1490px;"><p class="vanilla-image-block" style="padding-top:100.67%;"><img id="anfQB46xz27Fy6KWn9ysqX" name="Ninja Flexflame" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/anfQB46xz27Fy6KWn9ysqX.jpg" mos="" align="middle" fullscreen="" width="1490" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0GJ76517J" target="_blank" rel="nofollow" data-dimension112="69b10530-a21f-11f1-8a88-1d69015b22f5" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999">FlexFlame Propane Grill: was $1119 now $999</a></strong><br>The Ninja FlexFlame is Ninja's first attempt at a propane grill, and it offers all the ingeniuity you'd expect from a Ninja product. It's got a pellet chamber to infuse your food with an authentic smoked flavor, but can also sear and even be converted to a griddle with the use of add-ons.<a class="view-deal button" href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0GJ76517J" target="_blank" rel="nofollow" data-dimension112="69b10530-a21f-11f1-8a88-1d69015b22f5" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999">View Deal</a></p></div><h3 class="article-body__section" id="section-more-deals"><span>More Deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Home+Appliances&brands=Ninja%2CShark&min_discount_ratio=0.95&offer_type=all&view_mode=carousel&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=light_blue" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/LCxBaXIgdGFncywsLCwsLCwsLCwsLCwsDQosLFBvcnRhYmxlIGNoYXJnZXJzL3dpcmVsZXNzIGNoYXJnZXJzLCwsLCwsLCwsLCwsLCwNCiwsIldhdGVyIGJvdHRsZXMgKHN0YW5sZXlzLCBPd2FsYSwgSHlkcm8gZmxhc2ssIFlldGkpIiwsLCwsLCwsLCwsLCwsDQosLEhhbmQgaGVsZCBmYW5zLCwsLCwsLCwsLCwsLCwNCiwsLCwsLCwsLCwsLCwsLCwNCmhvbWUgb2ZmaWNlLCxvZmZpY2UgY2hhaXJzLCwsLCwsLCwsLCwsLCwNCiwsc3RhbmRpbmcgZGVza3MsLCwsLCwsLCwsLCwsLA0KLCxtb25pdG9ycywsLCwsLCwsLCwsLCwsDQosLEtleWJvYXJkcywsLCwsLCwsLCwsLCwsDQosLGRvY2tpbmcgc3RhdGlvbiwsLCwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpHYW1pbmcsLENvbnNvbGVzLCwsLCwsLCwsLCwsLCwNCiwsQWNjZXNzb3JpZXMsLCwsLCwsLCwsLCwsLA0KLCxHYW1lcywsLCwsLCwsLCwsLCwsDQosLENvdWxkIGluY2x1ZGUgTGVnbz8sLCwsLCwsLCwsLCwsLA==")));              const text = preloadedCSV;              const parsed = parseCSV(text);                            const rowsByName = {};              let filterStart = -1;              parsed.forEach((rc, i) => {                 if (rc && rc.length > 0 && rc[0]) rowsByName[rc[0]] = rc;                 if (rc && rc.length > 0 && rc[0] === 'Filter buttons') filterStart = i;              });                            const cols = [];              if(rowsByName['Roundel text']) {                const headerRow = rowsByName['Roundel text'];                for(let col = 1; col < headerRow.length; col++) {                   let label = headerRow[col];                   if (!label) continue;                                      let q = rowsByName['Search Query'] && rowsByName['Search Query'][col] ? rowsByName['Search Query'][col] : '';                   let img = rowsByName['Roundel image'] && rowsByName['Roundel image'][col] ? rowsByName['Roundel image'][col] : '';                   let ds = rowsByName['Discount Amount'] && rowsByName['Discount Amount'][col] ? rowsByName['Discount Amount'][col] : '';                   let pr = rowsByName['Price Range'] && rowsByName['Price Range'][col] ? rowsByName['Price Range'][col] : '';                   let rt = rowsByName['Retailer'] && rowsByName['Retailer'][col] ? rowsByName['Retailer'][col] : '';                   let ot = rowsByName['Offer Type'] && rowsByName['Offer Type'][col] ? rowsByName['Offer Type'][col] : '';                                      let filters = [];                   if(filterStart > 0) {                     for(let r = filterStart + 1; r < parsed.length; r++) {                         if(!parsed[r] || parsed[r][0] === 'Notes' || parsed[r][0] === 'Categories to consider') break;                         let f = parsed[r][col];                         if(f) filters.push(f);                     }                   }                   cols.push({ label, img, q, ds, pr, rt, ot, filters });                }              }              this.carouselData = cols;              if (this.carouselData && this.carouselData.length > 0) {                 const isMatched = this.carouselData.some(c => c.q === this.currentQuery || c.label === this.currentQuery);                 if (!isMatched) {                    const first = this.carouselData[0];                    this.currentQuery = first.q || first.label;                    if (this.priceFilter) this.priceFilter.value = 'all';                    if (this.customPriceMin) this.customPriceMin.value = '';                    if (this.customPriceMax) this.customPriceMax.value = '';                    let dPr = first.pr || 'all';                    if (typeof dPr === 'string' && dPr !== 'all') {                       let prLower = dPr.toLowerCase();                       if (prLower.includes('min') || prLower.includes('over')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else if (prLower.includes('max') || prLower.includes('under')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       }                    }                    let dAm = '0';                    if(first.ds && typeof first.ds === 'string') {                       let m = first.ds.match(/(\d+)/);                       if(m) dAm = m[1];                    }                    if (this.discountFilter) this.discountFilter.value = dAm;                    if (this.offerTypeSelect) this.offerTypeSelect.value = first.ot || '';                    if (this.retailerSelect) this.retailerSelect.value = first.rt || '';                    this.selectedBrands = [];                    if (this.brandDropdown) {                        const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                        chks.forEach(chk => chk.checked = false);                    }                    if (this.searchInput) this.searchInput.value = this.currentQuery;                 }              }              this.renderCarouselUI();          } catch(e){ console.warn(e); }        }                renderCarouselUI() {           const roundelWrapper = this.root.querySelector('.tg-df-carousel-roundels');           if(!roundelWrapper || !this.carouselData) return;                      let html = '';           this.carouselData.forEach(r => {              const q = r.q || r.label;              const isActive = (this.currentQuery === q || this.currentQuery === r.label) ? 'active' : '';              const imgHtml = r.img ? `\x3Cimg src="${r.img}" alt="${r.label}" />` : `\x3Csvg width="32" height="32" fill="#1F69FF" viewBox="0 0 24 24">\x3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\x3C/svg>`;              html += `                \x3Cdiv class="tg-df-roundel tg-df-carousel-cat ${isActive}" data-label="${this.escapeHTML(r.label)}">                  \x3Cdiv class="tg-df-roundel-img-box">${imgHtml}\x3C/div>                  \x3Cspan class="tg-df-roundel-label">${this.escapeHTML(r.label)}\x3C/span>                \x3C/div>              `;           });           roundelWrapper.innerHTML = html;                      // Rebind clicks           const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');           roundels.forEach(rNode => {             rNode.addEventListener('click', () => {                const r = this.carouselData.find(c => c.label === rNode.getAttribute('data-label'));                 if(!r) return;                                  if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: r.label,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = r.q || r.label;                const labelTitle = this.root.querySelector('#tg-df-carousel-title-label');                if (labelTitle) labelTitle.textContent = 'Best ' + this.currentQuery;                if (this.priceFilter) this.priceFilter.value = 'all';                if (this.customPriceMin) this.customPriceMin.value = '';                if (this.customPriceMax) this.customPriceMax.value = '';                let dPr = r.pr || 'all';                if (typeof dPr === 'string' && dPr !== 'all') {                   let prLower = dPr.toLowerCase();                   if (prLower.includes('min') || prLower.includes('over')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMin) this.customPriceMin.value = m[1];                   } else if (prLower.includes('max') || prLower.includes('under')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMax) this.customPriceMax.value = m[1];                   }                }                                let discountAmount = '0';                if(r.ds && typeof r.ds === 'string') {                   let m = r.ds.match(/(\d+)/);                   if(m) discountAmount = m[1];                }                if (this.discountFilter) this.discountFilter.value = discountAmount;                if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                                // Clear brands                    this.selectedBrands = [];                    if (this.brandDropdown) {                    const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                    chks.forEach(chk => chk.checked = false);                }                                if (this.searchInput) this.searchInput.value = this.currentQuery;                                roundels.forEach(ro => ro.classList.remove('active'));                if (rNode) rNode.classList.add('active');                                this.renderCarouselFilters(r);                this.fetchDeals(this.currentQuery);             });           });                      // Auto-highlight active           const activeR = this.carouselData.find(c => c.q === this.currentQuery || c.label === this.currentQuery);           if(activeR) this.renderCarouselFilters(activeR);        }                renderCarouselFilters(r) {           const filtersWrap = this.root.querySelector('.tg-df-carousel-filters-wrap');           if(!filtersWrap) return;                      let html = `\x3Cbutton class="tg-df-carousel-filter-btn" data-type="all">All\x3C/button>`;                      r.filters.forEach(f => {              let fL = f.toLowerCase();              let icon = '';              let logic = `data-type="custom" data-v="${this.escapeHTML(f)}"`;              if (fL === 'amazon deals' || fL === 'prime deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>`;              } else if (fL === 'lightning deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>`;              } else {                 if (fL.includes('lightning')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap">\x3Cpolygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2">\x3C/polygon>\x3C/svg>`;                 } else if (fL.includes('% off')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>`;                 } else if (fL.includes('under') || fL.includes('min ')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>`;                 }                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>${icon} ${this.escapeHTML(f)}\x3C/button>`;              }           });                      filtersWrap.innerHTML = html;                      const btns = filtersWrap.querySelectorAll('button');           btns.forEach(b => {             b.addEventListener('click', () => {                const type = b.getAttribute('data-type');                if (type === 'custom') {                   const v = b.getAttribute('data-v');                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-custom-${(v||'').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`, name: 'Custom Filter', label: v });                }                if (type === 'all') {                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: 'filter-clear-all', name: 'Clear all', label: 'Clear all filters' });                   // reset everything                   btns.forEach(btn => btn.classList.remove('active'));                   b.classList.add('active');                                      // Reset prices                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   if (this.brandDropdown) {                     const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                     chks.forEach(chk => chk.checked = false);                   }                } else {                   const v = b.getAttribute('data-v');                   const fL = v.toLowerCase();                                      let mapRet = ['amazon', 'walmart', 'best buy', 'target', 'john lewis', 'currys', 'argos'];                   const getCategory = (s) => {                      if (s === 'lightning deals' || s === 'amazon deals' || s === 'prime deals') return 'offer';                      if (s.includes('% off')) return 'discount';                      if (s.includes('under') || s.includes('over') || s.includes('min') || s.includes('max')) return 'price';                      if (mapRet.includes(s)) return 'retailer';                      return 'brand';                   };                   const cat = getCategory(fL);                   const wasActive = b.classList.contains('active');                   if (cat !== 'brand') {                      btns.forEach(btn => {                          if (btn === b) return;                          if (btn.getAttribute('data-type') === 'all') return;                          const bV = btn.getAttribute('data-v');                          if (!bV) return;                          if (getCategory(bV.toLowerCase()) === cat) btn.classList.remove('active');                      });                   }                   if (wasActive) b.classList.remove('active');                   else b.classList.add('active');                   let anyActive = Array.from(btns).some(btn => btn !== btns[0] && btn.classList.contains('active'));                   if (!anyActive) {                       btns[0].click();                       return;                   } else {                       btns[0].classList.remove('active');                   }                                      if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   btns.forEach(btn => {                       if (!btn.classList.contains('active') || btn.getAttribute('data-type') === 'all') return;                       const vv = btn.getAttribute('data-v');                       const vl = vv.toLowerCase();                                              if (vl === 'lightning deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_lightning';                       } else if (vl === 'amazon deals' || vl === 'prime deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_prime';                       } else if (vl.includes('% off')) {                          let m = vl.match(/(\d+)%/);                          if (m && this.discountFilter) this.discountFilter.value = m[1];                       } else if (vl.includes('under') || vl.includes('max')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       } else if (vl.includes('min') || vl.includes('over')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else {                          let foundR = mapRet.find(x => x === vl);                          if (foundR) {                             let realR = ['Amazon', 'Walmart', 'Best Buy', 'Target', 'John Lewis', 'Currys', 'Argos'].find(x => x.toLowerCase() === vl);                             if (this.retailerSelect) this.retailerSelect.value = realR;                          } else {                             this.selectedBrands.push(vv);                          }                       }                   });                                      if (this.brandDropdown) {                       const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                       chks.forEach(c => c.checked = this.selectedBrands.includes(c.value));                   }                                      if (r.pr && typeof r.pr === 'string') {                       let prL = r.pr.toLowerCase();                       if (prL.includes('under $')) {                           let m = prL.match(/under \$(\d+)/i);                           if (m && this.customPriceMax && !this.customPriceMax.value) this.customPriceMax.value = m[1];                       }                   }                }                                this.fetchDeals(this.currentQuery);             });           });                      // default to highlighting first           btns[0].classList.add('active');        }async fetchDeals(query, append = false) {          if (!append) {             this.showLoading();             this.deals = [];             this.displayLimit = this.getRowsLimit();          } else {             this.displayLimit += this.getRowsLimit();          }                    try {            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad(append);            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query, append);               } else {                 await this.fetchHawkDeals(query, append);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query, append);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const url = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let dynamicBrandsCounts = {};                    targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                   dynamicBrandsCounts[data.brand] = (dynamicBrandsCounts[data.brand] || 0) + 1;                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || ''                });             });          });                    const airedaleBrandsList = Object.keys(dynamicBrandsCounts).map(b => ({              formatted_value: b,              count: dynamicBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query, append = false) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (append && this.deals.length > 0) {            url.searchParams.append('offset', this.deals.length.toString());          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : (this.getViewMode() === 'savings_squad' ? 'date_desc' : 'discount_desc');          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin-top:8px;">${_div}                ${_div}              ${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)} (${this.airedaleTagCounts[tag] || 0})${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = isSavingsSquadMode ? 'View Deal' : 'Check Price';              if (isSavingsSquadMode) {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                  ${_div}                `;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = `                \x3Cdiv class="tg-df-card-merchant-wrapper">                  \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                ${_div}                \x3Cdiv class="tg-df-card-price-group">                  ${isSavingsSquadMode ? '' : `                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                  `}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description">\x3Cp>${this.escapeHTML(deal.description)}${_p}${_div}` : ''}                      ${_div}                    ${_div}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>${deal.isCheckPrice ? 'Check Price' : 'View Deal'}${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cp style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 12px; line-height: 1.4;">${this.escapeHTML(deal.description)}${_p}` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${priceGroupHtml}                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta ${isSavingsSquadMode ? 'tg-df-cta-savings-squad' : ''}" style="text-decoration: none;">${ctaText}${_a}                  ${_div}                ${_div}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit || ((this.getViewMode() === 'carousel' || this.getViewMode() === 'auto') && displayDeals.length > 0 && displayDeals.length % ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12) === 0)) {            if (this.getViewMode() === 'carousel') {               dealsHtml += `                 \x3Cbutton type="button" class="tg-df-load-more-card tg-df-load-more">                   \x3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;">\x3Cpath d="M5 12h14">\x3C/path>\x3Cpath d="m12 5 7 7-7 7">\x3C/path>\x3C/svg>                   Load More                 ${_button}               `;            } else {               dealsHtml += `                 \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                   \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer; display: flex; align-items: center;">Load More${_button}                 ${_div}               `;            }          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let rightChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             let leftChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-left');             if (this.getViewMode() === 'carousel') {                 // The observer set up in setupScrollListeners handles visibility.                 if (rightChevron) rightChevron.style.display = 'flex';                 if (leftChevron) leftChevron.style.display = 'none'; // reset correctly             } else {                 if (rightChevron) rightChevron.style.display = 'none';                 if (leftChevron) leftChevron.style.display = 'none';             }          }                    const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', async () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              if (displayDeals.length <= this.displayLimit) {                 loadMoreBtn.innerHTML = `                  <svg class="tg-df-spinner" style="width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 8px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/></svg>                  Loading...                 `;                 loadMoreBtn.disabled = true;                 await this.fetchDeals(this.currentQuery, true);              } else {                 this.displayLimit += this.getRowsLimit();                 this.render();              }            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            setTimeout(() => {               if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');               document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));            }, 50);          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Enter the Vitamix Nano: Could it be the answer to perfectly blended single-serve smoothies? ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Vitamix makes some of the <a href="https://www.tomsguide.com/best-picks/best-blenders">best blenders</a> on the market — our testing at Tom’s Guide consistently proves it — but the brand’s premium price tags often put them out of reach.</p><p>However, that just changed with the launch of the Vitamix Nano. The brand is re-entering the single-serve market with a compact, personal blender design for the health-conscious in mind, and it’s available for <a href="https://www.vitamix.com/us/en_us" target="_blank" rel="nofollow">$199 at Vitamix</a>.</p><h2 id="single-serve-smoothies">Single-serve smoothies</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="bYv4sFXPAXKQ25oCFkmWTc" name="Vitamix Nano" alt="Vitamix Nano" src="https://cdn.mos.cms.futurecdn.net/bYv4sFXPAXKQ25oCFkmWTc.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Vitamix)</span></figcaption></figure><p>The compact Nano isn’t designed for big elaborate feasts. Instead, it focuses on the essentials: creating smoothies and frozen drinks with a small footprint that won’t overcrowd your countertop.</p><p>Although you don’t get all the multi-functions of other Vitamix blenders, such as the <a href="https://www.tomsguide.com/home/home-appliances/vitamix-ascent-x2-blender-review">Vitamix Ascent X2</a>, which takes the top spot in our <a href="https://www.tomsguide.com/best-picks/best-blenders">best blenders buying guide</a>, you don’t get short-changed on performance. The Vitamix Nano has a single purpose.</p><div class="product"><a data-dimension112="196edacc-9fac-11f1-a0b9-e9e3c8c2a9ea" data-action="Deal Block" data-label="Nano" data-dimension48="Nano" data-dimension25="$199" href="https://www.vitamix.com/us/en_us/products/nano" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="qRJet8VAb3tngGTe3hwQ39" name="Vitamix Nano" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qRJet8VAb3tngGTe3hwQ39.png" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.vitamix.com/us/en_us/products/nano" target="_blank" rel="nofollow" data-dimension112="196edacc-9fac-11f1-a0b9-e9e3c8c2a9ea" data-action="Deal Block" data-label="Nano" data-dimension48="Nano" data-dimension25="$199">Nano: $199</a></strong><br>The Vitamix personal, single-serve blender that thinks while it blends. Select the SmoothiePilot setting, and the blender will automatically adjust the speed and timing to achieve the right texture. Or, opt for the variable speed for a personalized choice.  <a class="view-deal button" href="https://www.vitamix.com/us/en_us/products/nano" target="_blank" rel="nofollow" data-dimension112="196edacc-9fac-11f1-a0b9-e9e3c8c2a9ea" data-action="Deal Block" data-label="Nano" data-dimension48="Nano" data-dimension25="$199">View Deal</a></p></div><h2 id="intelligent-blending">Intelligent blending</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3NBt4nQV6d8iYABTmherzA" name="Vitamix Nano" alt="Vitamix Nano" src="https://cdn.mos.cms.futurecdn.net/3NBt4nQV6d8iYABTmherzA.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Vitamix)</span></figcaption></figure><p>At the heart of the Vitamix Nano is SmoothiePilot — the brand’s first adaptive smoothie feature. So what exactly does that mean? It senses what’s happening inside the cup and automatically selects the optimal blend program, adjusting the speed and duration to deliver a smooth result, whether making a smoothie or frozen cocktail.</p><p>Laura Ostenkamp, vice president of sales and marketing at Vitamix, says, in short, it "blends every time with no guesswork required."</p><p>Does this put an end to pausing mid-blend to check and stir the ingredients, and to finding unexpected lumps in your cocktails? We’ll have to see how it performs once we receive a unit to test. </p><h2 id="the-vitamix-nano-what-do-you-get">The Vitamix Nano: What do you get?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZfwKLjkxXTXkmTGBABPzQc" name="Vitamix Nano" alt="Vitamix Nano" src="https://cdn.mos.cms.futurecdn.net/ZfwKLjkxXTXkmTGBABPzQc.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Vitamix)</span></figcaption></figure><p>So what does this compact <strong>20-ounce cup</strong> blender offer, and is it worth paying $199 for the privilege? Vitamix says it comes with a high-powered <strong>1,000-watt motor</strong> and <strong>hardened stainless steel blades</strong> to tackle tough ingredients like ice and frozen fruit. </p><p>What’s more, the technology takes away all the hard work, making the controls simple: select <strong>SmoothiePilot</strong> for a perfectly smooth drink, or select <strong>variable speed</strong> for your preferred texture. </p><p>You also get <strong>two cups with lids</strong>, so you can blend your drinks and go, or keep one in the refrigerator for later.<br><br>Cleaning a blender can be notoriously tricky, but once you’ve made your drink, clean up is easy. According to Vitamix, it <strong>self-cleans in 30 seconds</strong>; just add hot water and a drop of dish soap; there’s no need to disassemble the unit. But, for an easy life, it’s also <strong>dishwasher-safe</strong>. </p><p>And if you’re wondering whether you have enough space for another gadget on your countertop, the Vitamix Nano is mini. It measures just 8.5 inches x 5.5 inches at the base, and stands 15.3 inches tall. </p><p>The only choice you’ll have to make apart from selecting SmoothiePilot or the variable control is whether to buy the white or black version. My money is on the black, although I’d like to see a few punchy candy colors introduced in the future.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchen-dining/vitamix-just-released-an-entry-level-version-of-our-all-time-favorite-blender">Vitamix just released an entry-level version of our all-time favorite blender</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/ninja-blast-max-review">I made smoothies, shakes and pancake batter in Ninja's most powerful portable blender — here's my verdict</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/i-threw-out-my-nutribullet-blender-after-testing-this-beast-personal-blender-heres-why">I threw out my Nutribullet blender after testing this Beast personal blender — here's why</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/enter-the-vitamix-nano-could-it-be-the-answer-to-perfectly-blended-single-serve-smoothies</link>
                                                                            <description>
                            <![CDATA[ Vitamix just launched a new personal blender with intelligent tech: Is it worth $199? ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">9o2Ahgvh7Wwc68qQbD48Tk</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/YgkcnUArksceTGzwaftMU3-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Mon, 24 Aug 2026 13:53:29 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ camilla.sharman@futurenet.com (Camilla Sharman) ]]></author>                    <dc:creator><![CDATA[ Camilla Sharman ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/nY4nvWzofHKHpvzAqN5LVH.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Camilla is the Homes Staff Writer and covers everything to do with homes and gardens. She has a wealth of editorial experience, mounting over 30 years, and covers news and features, tests products for reviews and compiles buying guides.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Her work has appeared in business and consumer titles, including Ideal Home, Real Homes, House Beautiful, Homebuilding &amp;amp; Renovation, and Kitchen &amp;amp; Bathroom Business. She’s even appeared on the cover of Your Home, writing about her own house renovation.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Although she’s obsessed with decorating her home, she also enjoys baking and trying out the latest kitchen appliances. But when she’s not inside, you’ll find her pottering about in her yard, tending to her vegetable patch or taking in her prized hydrangeas. She also enjoys keeping fit, and if she&#039;s not on a spin bike trying to keep up with the class, she&#039;ll be in the pool, or trying to perfect her headstand in a yoga class.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/png" url="https://cdn.mos.cms.futurecdn.net/YgkcnUArksceTGzwaftMU3-1280-80.png">
                                                            <media:credit><![CDATA[Vitamix]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Vitamix Nano in black]]></media:description>                                                            <media:text><![CDATA[Vitamix Nano in black]]></media:text>
                                <media:title type="plain"><![CDATA[Vitamix Nano in black]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/YgkcnUArksceTGzwaftMU3-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Vitamix makes some of the <a href="https://www.tomsguide.com/best-picks/best-blenders">best blenders</a> on the market — our testing at Tom’s Guide consistently proves it — but the brand’s premium price tags often put them out of reach.</p><p>However, that just changed with the launch of the Vitamix Nano. The brand is re-entering the single-serve market with a compact, personal blender design for the health-conscious in mind, and it’s available for <a href="https://www.vitamix.com/us/en_us" target="_blank" rel="nofollow">$199 at Vitamix</a>.</p><h2 id="single-serve-smoothies">Single-serve smoothies</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="bYv4sFXPAXKQ25oCFkmWTc" name="Vitamix Nano" alt="Vitamix Nano" src="https://cdn.mos.cms.futurecdn.net/bYv4sFXPAXKQ25oCFkmWTc.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Vitamix)</span></figcaption></figure><p>The compact Nano isn’t designed for big elaborate feasts. Instead, it focuses on the essentials: creating smoothies and frozen drinks with a small footprint that won’t overcrowd your countertop.</p><p>Although you don’t get all the multi-functions of other Vitamix blenders, such as the <a href="https://www.tomsguide.com/home/home-appliances/vitamix-ascent-x2-blender-review">Vitamix Ascent X2</a>, which takes the top spot in our <a href="https://www.tomsguide.com/best-picks/best-blenders">best blenders buying guide</a>, you don’t get short-changed on performance. The Vitamix Nano has a single purpose.</p><div class="product"><a data-dimension112="196edacc-9fac-11f1-a0b9-e9e3c8c2a9ea" data-action="Deal Block" data-label="Nano" data-dimension48="Nano" data-dimension25="$199" href="https://www.vitamix.com/us/en_us/products/nano" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="qRJet8VAb3tngGTe3hwQ39" name="Vitamix Nano" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qRJet8VAb3tngGTe3hwQ39.png" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.vitamix.com/us/en_us/products/nano" target="_blank" rel="nofollow" data-dimension112="196edacc-9fac-11f1-a0b9-e9e3c8c2a9ea" data-action="Deal Block" data-label="Nano" data-dimension48="Nano" data-dimension25="$199">Nano: $199</a></strong><br>The Vitamix personal, single-serve blender that thinks while it blends. Select the SmoothiePilot setting, and the blender will automatically adjust the speed and timing to achieve the right texture. Or, opt for the variable speed for a personalized choice.  <a class="view-deal button" href="https://www.vitamix.com/us/en_us/products/nano" target="_blank" rel="nofollow" data-dimension112="196edacc-9fac-11f1-a0b9-e9e3c8c2a9ea" data-action="Deal Block" data-label="Nano" data-dimension48="Nano" data-dimension25="$199">View Deal</a></p></div><h2 id="intelligent-blending">Intelligent blending</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3NBt4nQV6d8iYABTmherzA" name="Vitamix Nano" alt="Vitamix Nano" src="https://cdn.mos.cms.futurecdn.net/3NBt4nQV6d8iYABTmherzA.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Vitamix)</span></figcaption></figure><p>At the heart of the Vitamix Nano is SmoothiePilot — the brand’s first adaptive smoothie feature. So what exactly does that mean? It senses what’s happening inside the cup and automatically selects the optimal blend program, adjusting the speed and duration to deliver a smooth result, whether making a smoothie or frozen cocktail.</p><p>Laura Ostenkamp, vice president of sales and marketing at Vitamix, says, in short, it "blends every time with no guesswork required."</p><p>Does this put an end to pausing mid-blend to check and stir the ingredients, and to finding unexpected lumps in your cocktails? We’ll have to see how it performs once we receive a unit to test. </p><h2 id="the-vitamix-nano-what-do-you-get">The Vitamix Nano: What do you get?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZfwKLjkxXTXkmTGBABPzQc" name="Vitamix Nano" alt="Vitamix Nano" src="https://cdn.mos.cms.futurecdn.net/ZfwKLjkxXTXkmTGBABPzQc.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Vitamix)</span></figcaption></figure><p>So what does this compact <strong>20-ounce cup</strong> blender offer, and is it worth paying $199 for the privilege? Vitamix says it comes with a high-powered <strong>1,000-watt motor</strong> and <strong>hardened stainless steel blades</strong> to tackle tough ingredients like ice and frozen fruit. </p><p>What’s more, the technology takes away all the hard work, making the controls simple: select <strong>SmoothiePilot</strong> for a perfectly smooth drink, or select <strong>variable speed</strong> for your preferred texture. </p><p>You also get <strong>two cups with lids</strong>, so you can blend your drinks and go, or keep one in the refrigerator for later.<br><br>Cleaning a blender can be notoriously tricky, but once you’ve made your drink, clean up is easy. According to Vitamix, it <strong>self-cleans in 30 seconds</strong>; just add hot water and a drop of dish soap; there’s no need to disassemble the unit. But, for an easy life, it’s also <strong>dishwasher-safe</strong>. </p><p>And if you’re wondering whether you have enough space for another gadget on your countertop, the Vitamix Nano is mini. It measures just 8.5 inches x 5.5 inches at the base, and stands 15.3 inches tall. </p><p>The only choice you’ll have to make apart from selecting SmoothiePilot or the variable control is whether to buy the white or black version. My money is on the black, although I’d like to see a few punchy candy colors introduced in the future.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchen-dining/vitamix-just-released-an-entry-level-version-of-our-all-time-favorite-blender">Vitamix just released an entry-level version of our all-time favorite blender</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/ninja-blast-max-review">I made smoothies, shakes and pancake batter in Ninja's most powerful portable blender — here's my verdict</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/i-threw-out-my-nutribullet-blender-after-testing-this-beast-personal-blender-heres-why">I threw out my Nutribullet blender after testing this Beast personal blender — here's why</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ People are mixing coffee with Diet Coke (yes, really) — I'm an ex-barista, and I tried the viral 'fridge cigarette' ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the twenty-first episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>Let me put you onto something weird: the "Fridge cigarette." I've seen this term used to refer to a cold, crisp can of Diet Coke, but recently, another meaning has arisen: Diet Coke + espresso. As director of <a href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a> and coffee nerd, I had to try this out. What do you mean, people are mixing coffee and Diet Coke? How can that possibly taste good?</p><p>To make it, I bought a simple can of Diet Coke and made a shot of espresso using my <a href="https://www.tomsguide.com/home/coffee-makers/sage-breville-bambino-review">Breville Bambino</a>. Then, I mixed them — simple as that. No fiddling with measurements or layering cream on top like Starbucks's Sweet Cream Cold Brew.</p><p>So, is the "Fridge cigarette" worth your time? Let's get into it. If you want me to try out more weird recipes, comment them down below, and I'll be sure to include them in the next Coffee Lab video.</p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7677698671265303822" data-video-id="7677698671265303822" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ city lights drift - GIN CUBE" href="https://www.tiktok.com/music/city-lights-drift-7599489479816661009">♬ city lights drift - GIN CUBE</a></section>                    </blockquote></div>                <h2 id="how-to-make-a-39-fridge-cigarette-39">How to make a 'fridge cigarette'</h2><p>As I said earlier, making a fridge cigarette is super easy.</p><section class="howto-block">                    <h3>1. Procure a can of Diet Coke</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/Xq5LMnptjfrhuwg3KZCCh9.jpg"                                        alt="a photo of the viral fridge cigarette coffee + diet coke being made"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/Xq5LMnptjfrhuwg3KZCCh9.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                                    </section><section class="howto-block">                    <h3>2. Pull a shot of espresso</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/jRAVgmnzQ5oEMAgJYWXVNb.png"                                        alt="a photo of an espresso made on the breville bambino / sage bambino "                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/jRAVgmnzQ5oEMAgJYWXVNb.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                                    </section><section class="howto-block">                    <h3>3. Mix them together</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/JJfW8FaoLzipNUwyke4Ys9.jpg"                                        alt="a photo of the viral fridge cigarette coffee + diet coke being made"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/JJfW8FaoLzipNUwyke4Ys9.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                                    </section><h2 id="how-does-it-taste">How does it taste?</h2><p>It doesn't look appealing, but the fridge cigarette actually tastes... good? </p><p>If you like Espresso Martinis, you'll love the fridge cigarette. It took me a moment to identify the flavor — at first I thought Guinness, but then realized that wasn't the specific flavor I was tasting. It's sweet and vanilla-y, with a rich, malty afternote rounding it out.</p><p>I mean, it's not massively shocking. Diet Coke = vanilla. Espresso = coffee. Espresso Martini = vanilla, coffee. All the same flavors are going on here. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5071px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="dXphpzD79anUdiPYYPbbt9" name="fridge cigarette final" alt="a photo of the viral fridge cigarette coffee + diet coke being made" src="https://cdn.mos.cms.futurecdn.net/dXphpzD79anUdiPYYPbbt9.jpg" mos="" align="middle" fullscreen="" width="5071" height="2852" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>What about caffeine content? I mean, if you're super exhausted, this might be a way to bring you back to life. As soon as I drank about half, my hands started shaking, and I was acutely aware of the fluorescent lights in the studio. This isn't a drink I'd recommend if you're sensitive to caffeine. This is only for hardened caffeinators.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ODqdlX"></div>                            </div>                            <script src="https://kwizly.com/embed/ODqdlX.js" async></script><p>As always, let me know if you have any questions or if there are any other weird coffee drinks you want me to taste-test next. Stay tuned for the next episode of The Coffee Lab, landing at a YouTube, TikTok, and Instagram near you.</p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/overwhelmed-by-coffee-jargon-here-are-the-5-key-espresso-terms-you-need-to-know"><strong>Overwhelmed by coffee jargon? Here are the 5 key espresso terms you need to know</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/im-an-ex-starbucks-barista-you-can-make-the-iced-brown-sugar-oatmilk-shaken-espresso-at-home-for-a-fraction-of-the-cost"><strong>I'm an ex-Starbucks barista — you can make the Iced Brown Sugar Oatmilk Shaken Espresso at home for a fraction of the cost</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/people-are-mixing-coffee-with-diet-coke-yes-really-im-an-ex-barista-and-i-tried-the-viral-fridge-cigarette</link>
                                                                            <description>
                            <![CDATA[ On the 21st episode of The Coffee Lab, I'm trying the viral "Fridge Cigarette" coffee cocktail. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">N838JhoZDPfsJuvPKEtoM3</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/rPFq2zqVWgJdfoc8oQK4Sg-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 24 Aug 2026 05:00:00 +0000</pubDate>                                                                                                                                <updated>Tue, 25 Aug 2026 12:34:06 +0000</updated>
                                                                                                                                            <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/rPFq2zqVWgJdfoc8oQK4Sg-1280-80.jpg">
                                                            <media:credit><![CDATA[Erin Bashford]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[a photo of a diet coke with the coffee lab logo]]></media:description>                                                            <media:text><![CDATA[a photo of a diet coke with the coffee lab logo]]></media:text>
                                <media:title type="plain"><![CDATA[a photo of a diet coke with the coffee lab logo]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/rPFq2zqVWgJdfoc8oQK4Sg-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the twenty-first episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>Let me put you onto something weird: the "Fridge cigarette." I've seen this term used to refer to a cold, crisp can of Diet Coke, but recently, another meaning has arisen: Diet Coke + espresso. As director of <a href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a> and coffee nerd, I had to try this out. What do you mean, people are mixing coffee and Diet Coke? How can that possibly taste good?</p><p>To make it, I bought a simple can of Diet Coke and made a shot of espresso using my <a href="https://www.tomsguide.com/home/coffee-makers/sage-breville-bambino-review">Breville Bambino</a>. Then, I mixed them — simple as that. No fiddling with measurements or layering cream on top like Starbucks's Sweet Cream Cold Brew.</p><p>So, is the "Fridge cigarette" worth your time? Let's get into it. If you want me to try out more weird recipes, comment them down below, and I'll be sure to include them in the next Coffee Lab video.</p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7677698671265303822" data-video-id="7677698671265303822" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ city lights drift - GIN CUBE" href="https://www.tiktok.com/music/city-lights-drift-7599489479816661009">♬ city lights drift - GIN CUBE</a></section>                    </blockquote></div>                <h2 id="how-to-make-a-39-fridge-cigarette-39">How to make a 'fridge cigarette'</h2><p>As I said earlier, making a fridge cigarette is super easy.</p><section class="howto-block">                    <h3>1. Procure a can of Diet Coke</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/Xq5LMnptjfrhuwg3KZCCh9.jpg"                                        alt="a photo of the viral fridge cigarette coffee + diet coke being made"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/Xq5LMnptjfrhuwg3KZCCh9.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                                    </section><section class="howto-block">                    <h3>2. Pull a shot of espresso</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/jRAVgmnzQ5oEMAgJYWXVNb.png"                                        alt="a photo of an espresso made on the breville bambino / sage bambino "                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/jRAVgmnzQ5oEMAgJYWXVNb.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                                    </section><section class="howto-block">                    <h3>3. Mix them together</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/JJfW8FaoLzipNUwyke4Ys9.jpg"                                        alt="a photo of the viral fridge cigarette coffee + diet coke being made"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/JJfW8FaoLzipNUwyke4Ys9.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                                    </section><h2 id="how-does-it-taste">How does it taste?</h2><p>It doesn't look appealing, but the fridge cigarette actually tastes... good? </p><p>If you like Espresso Martinis, you'll love the fridge cigarette. It took me a moment to identify the flavor — at first I thought Guinness, but then realized that wasn't the specific flavor I was tasting. It's sweet and vanilla-y, with a rich, malty afternote rounding it out.</p><p>I mean, it's not massively shocking. Diet Coke = vanilla. Espresso = coffee. Espresso Martini = vanilla, coffee. All the same flavors are going on here. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5071px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="dXphpzD79anUdiPYYPbbt9" name="fridge cigarette final" alt="a photo of the viral fridge cigarette coffee + diet coke being made" src="https://cdn.mos.cms.futurecdn.net/dXphpzD79anUdiPYYPbbt9.jpg" mos="" align="middle" fullscreen="" width="5071" height="2852" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>What about caffeine content? I mean, if you're super exhausted, this might be a way to bring you back to life. As soon as I drank about half, my hands started shaking, and I was acutely aware of the fluorescent lights in the studio. This isn't a drink I'd recommend if you're sensitive to caffeine. This is only for hardened caffeinators.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ODqdlX"></div>                            </div>                            <script src="https://kwizly.com/embed/ODqdlX.js" async></script><p>As always, let me know if you have any questions or if there are any other weird coffee drinks you want me to taste-test next. Stay tuned for the next episode of The Coffee Lab, landing at a YouTube, TikTok, and Instagram near you.</p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/overwhelmed-by-coffee-jargon-here-are-the-5-key-espresso-terms-you-need-to-know"><strong>Overwhelmed by coffee jargon? Here are the 5 key espresso terms you need to know</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/im-an-ex-starbucks-barista-you-can-make-the-iced-brown-sugar-oatmilk-shaken-espresso-at-home-for-a-fraction-of-the-cost"><strong>I'm an ex-Starbucks barista — you can make the Iced Brown Sugar Oatmilk Shaken Espresso at home for a fraction of the cost</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve swapped to an induction cooktop from gas — 3 things I love, and 1 thing you need to know ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I’ve been boring everyone in earshot about my new kitchen. It’s been on my wishlist for a couple of years, so I’ve had plenty of time to work out exactly what I want for the cabinets and countertops. But when it came to appliances, there was one non-negotiable that topped the list.</p><p>Having relied on a gas cooktop for nearly 30 years, I was <a href="https://www.tomsguide.com/home/home-appliances/induction-vs-gas-cooktop-which-is-best-for-you">ready to make a swap</a>. I was fed up with food boiling over and cleaning up around the rings and grates. </p><p>So I was more than ready to say goodbye to gas and welcome <a href="https://www.tomsguide.com/home/home-appliances/i-asked-6-culinary-pros-if-its-worth-switching-to-an-induction-cooktop-heres-the-verdict">induction</a> into my kitchen.  </p><p>It’s now one week since my new Hisense Hi8 induction cooktop was fitted (you can see from my images that I’m still working from a makeshift worktop), but in that short time I can honestly say I have no regrets.<br><br>Here I share three things I love and one thing you need to know before buying an induction cooktop.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-W5ryxO"></div>                            </div>                            <script src="https://kwizly.com/embed/W5ryxO.js" async></script><h3 class="article-body__section" id="section-3-things-i-love"><span>3 things I love</span></h3><h2 id="1-precision-cooking">1. Precision cooking</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="o8aL8VudjeXihLTGxPmgE" name="Hisense Hi8 Induction Cooktop" alt="Hisense Hi8 Induction Cooktop" src="https://cdn.mos.cms.futurecdn.net/o8aL8VudjeXihLTGxPmgE.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I was first introduced to <a href="https://www.tomsguide.com/home/I-tried-an-induction-cooktop-3-things-i-learned">induction cooking</a> during a press visit to Smeg’s test kitchen, and was a little nervous to start. I wasn’t sure what to expect, or how easy an induction cooktop would be to use. </p><p>It turns out I had nothing to fear, and apart from taking a few moments to get used to the cooking levels, I found it instinctive.</p><p>I've since tested several portable versions, including <a href="https://www.tomsguide.com/home/home-appliances/i-just-tested-smegs-portable-induction-cooktop-and-its-the-perfect-kitchen-sidekick">Smeg’s portable induction cooktop</a> and <a href="https://www.tomsguide.com/home/kitchen-dining/i-tested-ikeas-portable-induction-cooktop-and-its-a-game-changer-for-small-spaces">Ikea’s budget-friendly portable induction cooktop, </a>and I’ve experienced the same with each one: induction cooking gives you precision.</p><p>Twiddling the knobs on my gas cooktop to get the heat 'just right’ always felt like guesswork, and I was forever having to clean up the mess of boiled-over liquid.  </p><p>The controls on the Hisense induction cooktop make it super easy to get the exact heat you need.  What’s more, because your pans sit directly on the cooktop, without a rack above, heat loss is minimal. </p><h2 id="2-it-39-s-quick">2. It's quick</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iWFhWSrR9ySymc8xMnb6b5" name="Smeg-Power-Boost-boiling-water.jpg" alt="Power boost function on Smeg Portable Induction Cooktop" src="https://cdn.mos.cms.futurecdn.net/iWFhWSrR9ySymc8xMnb6b5.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>After years of cooking on a gas cooktop, I always relied on an electric kettle to preheat my pasta water. Trying to bring a large pot to a boil from cold on a gas burner felt like it took an eternity. </p><p>Since swapping to induction, I’ve noticed a massive difference in speed, and it hasn’t gone unnoticed by my son, who has taken a sudden interest in this new ‘gadget’. </p><p>Apart from noticing the increased speed when using the normal controls, you can increase the intensity with the power boost — or ‘P’ on the controls. Apart from being a feature on the Hisense induction cooktop, it’s even included within Smeg's portable version.</p><h2 id="3-clean-up-is-easy">3. Clean up is easy</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3062px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="mieDQ7imd3U3MHiTeroGVF" name="Induction cooktop" alt="Hisense Hi8 induction cooktop" src="https://cdn.mos.cms.futurecdn.net/mieDQ7imd3U3MHiTeroGVF.jpg" mos="" align="middle" fullscreen="" width="3062" height="1722" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I can forget about spending time after I’ve enjoyed a meal cleaning up my cooktop. The flat surface makes it easy to wipe over. There are no awkward surfaces to work around, and with precision control, there’s less chance of spills and burnt-on messes.</p><p>What’s more, the Hisense Hi8 has a matt finish, so unlike the majority of induction cooktops with shiny surfaces, fingerprint issues are also obsolete.</p><h3 class="article-body__section" id="section-1-thing-to-know"><span>1 thing to know</span></h3><h2 id="you-may-need-to-change-your-pans">You may need to change your pans</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3000px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="RhPPP4z4s47XPFwfwQRZR6" name="Induction cooktop-icon" alt="Induction cooktop with icon in background" src="https://cdn.mos.cms.futurecdn.net/RhPPP4z4s47XPFwfwQRZR6.jpg" mos="" align="middle" fullscreen="" width="3000" height="1687" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>I’d been using my previous pans for over 20 years, and they still have plenty of life left in them. However, as they are not induction-friendly, they simply won’t work.</p><p>Induction cooking works by passing an electric current through a coiled copper wire beneath the cooking surface. This creates magnetic energy that interacts with the cookware rather than the cooktop’s surface. For heat to be transferred, your pans need to be magnetic. That means using <a href="https://www.tomsguide.com/home/can-you-use-stainless-steel-pans-on-an-induction-cooktop-heres-what-the-experts-say">stainless steel</a> or <a href="https://www.tomsguide.com/home/does-an-induction-cooktop-work-with-cast-iron-pans-heres-what-the-experts-say">cast iron</a>. Aluminum and copper pans won’t work.</p><p>Today, this isn’t a big issue, as when I was shopping for my new pans, the majority were induction-compatible. But if you’re not sure what to buy, look out for a symbol resembling a coil. And if you're <em>still</em> not sure what to look for, drop me a question in the comments below and I'll be happy to answer.</p><div class="product"><a data-dimension112="004568aa-9d71-11f1-9fc2-77e131a793c7" data-action="Deal Block" data-label="Cookware, set of 6, stainless steel" data-dimension48="Cookware, set of 6, stainless steel" data-dimension25="$59" href="https://www.ikea.com/us/en/p/ikea-365-cookware-set-of-6-stainless-steel-70577908/?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:507px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="v5xMobVMWFvaSFQeBsxnmK" name="IKEA 365+ Cookware, set of 6, stainless steel pans" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/v5xMobVMWFvaSFQeBsxnmK.png" mos="" align="middle" fullscreen="" width="507" height="507" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/ikea-365-cookware-set-of-6-stainless-steel-70577908/?" target="_blank" rel="nofollow" data-dimension112="004568aa-9d71-11f1-9fc2-77e131a793c7" data-action="Deal Block" data-label="Cookware, set of 6, stainless steel" data-dimension48="Cookware, set of 6, stainless steel" data-dimension25="$59">Cookware, set of 6, stainless steel: $59</a></strong><br>IKEA's cookware set is made of stainless steel and is compatible with all cooktops, including induction. The three pans come with a lid and are dishwasher- and oven-safe. <a class="view-deal button" href="https://www.ikea.com/us/en/p/ikea-365-cookware-set-of-6-stainless-steel-70577908/?" target="_blank" rel="nofollow" data-dimension112="004568aa-9d71-11f1-9fc2-77e131a793c7" data-action="Deal Block" data-label="Cookware, set of 6, stainless steel" data-dimension48="Cookware, set of 6, stainless steel" data-dimension25="$59">View Deal</a></p></div><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/does-an-induction-cooktop-work-with-cast-iron-pans-heres-what-the-experts-say">Does an induction cooktop work with cast iron pans? Here’s what the experts say</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/i-switched-to-induction-cooking-heres-why-im-never-going-back">I switched to induction cooking in 2024 — here's why I'm never going back</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/are-you-damaging-your-induction-cooktop-heres-what-you-should-do-to-protect-it">Are you damaging your induction cooktop? Here’s what you should do to protect it</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/ive-swapped-to-an-induction-cooktop-from-gas-3-things-i-love-and-1-thing-you-need-to-know</link>
                                                                            <description>
                            <![CDATA[ Why I won’t shut up about my new induction cooktop — can I convert you too? ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">YndmPADhZuE8DmfPcUvh8T</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/js8zPtzd4HtqjF2nCSbPRX-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Sun, 23 Aug 2026 08:00:00 +0000</pubDate>                                                                                                                                <updated>Mon, 24 Aug 2026 08:24:00 +0000</updated>
                                                                                                                                            <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                                                                <author><![CDATA[ camilla.sharman@futurenet.com (Camilla Sharman) ]]></author>                    <dc:creator><![CDATA[ Camilla Sharman ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/nY4nvWzofHKHpvzAqN5LVH.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Camilla is the Homes Staff Writer and covers everything to do with homes and gardens. She has a wealth of editorial experience, mounting over 30 years, and covers news and features, tests products for reviews and compiles buying guides.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Her work has appeared in business and consumer titles, including Ideal Home, Real Homes, House Beautiful, Homebuilding &amp;amp; Renovation, and Kitchen &amp;amp; Bathroom Business. She’s even appeared on the cover of Your Home, writing about her own house renovation.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Although she’s obsessed with decorating her home, she also enjoys baking and trying out the latest kitchen appliances. But when she’s not inside, you’ll find her pottering about in her yard, tending to her vegetable patch or taking in her prized hydrangeas. She also enjoys keeping fit, and if she&#039;s not on a spin bike trying to keep up with the class, she&#039;ll be in the pool, or trying to perfect her headstand in a yoga class.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/png" url="https://cdn.mos.cms.futurecdn.net/js8zPtzd4HtqjF2nCSbPRX-1280-80.png">
                                                            <media:credit><![CDATA[Hisense]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Hisense Hi8 Induction Cooktop]]></media:description>                                                            <media:text><![CDATA[Hisense Hi8 Induction Cooktop]]></media:text>
                                <media:title type="plain"><![CDATA[Hisense Hi8 Induction Cooktop]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/js8zPtzd4HtqjF2nCSbPRX-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I’ve been boring everyone in earshot about my new kitchen. It’s been on my wishlist for a couple of years, so I’ve had plenty of time to work out exactly what I want for the cabinets and countertops. But when it came to appliances, there was one non-negotiable that topped the list.</p><p>Having relied on a gas cooktop for nearly 30 years, I was <a href="https://www.tomsguide.com/home/home-appliances/induction-vs-gas-cooktop-which-is-best-for-you">ready to make a swap</a>. I was fed up with food boiling over and cleaning up around the rings and grates. </p><p>So I was more than ready to say goodbye to gas and welcome <a href="https://www.tomsguide.com/home/home-appliances/i-asked-6-culinary-pros-if-its-worth-switching-to-an-induction-cooktop-heres-the-verdict">induction</a> into my kitchen.  </p><p>It’s now one week since my new Hisense Hi8 induction cooktop was fitted (you can see from my images that I’m still working from a makeshift worktop), but in that short time I can honestly say I have no regrets.<br><br>Here I share three things I love and one thing you need to know before buying an induction cooktop.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-W5ryxO"></div>                            </div>                            <script src="https://kwizly.com/embed/W5ryxO.js" async></script><h3 class="article-body__section" id="section-3-things-i-love"><span>3 things I love</span></h3><h2 id="1-precision-cooking">1. Precision cooking</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="o8aL8VudjeXihLTGxPmgE" name="Hisense Hi8 Induction Cooktop" alt="Hisense Hi8 Induction Cooktop" src="https://cdn.mos.cms.futurecdn.net/o8aL8VudjeXihLTGxPmgE.png" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I was first introduced to <a href="https://www.tomsguide.com/home/I-tried-an-induction-cooktop-3-things-i-learned">induction cooking</a> during a press visit to Smeg’s test kitchen, and was a little nervous to start. I wasn’t sure what to expect, or how easy an induction cooktop would be to use. </p><p>It turns out I had nothing to fear, and apart from taking a few moments to get used to the cooking levels, I found it instinctive.</p><p>I've since tested several portable versions, including <a href="https://www.tomsguide.com/home/home-appliances/i-just-tested-smegs-portable-induction-cooktop-and-its-the-perfect-kitchen-sidekick">Smeg’s portable induction cooktop</a> and <a href="https://www.tomsguide.com/home/kitchen-dining/i-tested-ikeas-portable-induction-cooktop-and-its-a-game-changer-for-small-spaces">Ikea’s budget-friendly portable induction cooktop, </a>and I’ve experienced the same with each one: induction cooking gives you precision.</p><p>Twiddling the knobs on my gas cooktop to get the heat 'just right’ always felt like guesswork, and I was forever having to clean up the mess of boiled-over liquid.  </p><p>The controls on the Hisense induction cooktop make it super easy to get the exact heat you need.  What’s more, because your pans sit directly on the cooktop, without a rack above, heat loss is minimal. </p><h2 id="2-it-39-s-quick">2. It's quick</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iWFhWSrR9ySymc8xMnb6b5" name="Smeg-Power-Boost-boiling-water.jpg" alt="Power boost function on Smeg Portable Induction Cooktop" src="https://cdn.mos.cms.futurecdn.net/iWFhWSrR9ySymc8xMnb6b5.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>After years of cooking on a gas cooktop, I always relied on an electric kettle to preheat my pasta water. Trying to bring a large pot to a boil from cold on a gas burner felt like it took an eternity. </p><p>Since swapping to induction, I’ve noticed a massive difference in speed, and it hasn’t gone unnoticed by my son, who has taken a sudden interest in this new ‘gadget’. </p><p>Apart from noticing the increased speed when using the normal controls, you can increase the intensity with the power boost — or ‘P’ on the controls. Apart from being a feature on the Hisense induction cooktop, it’s even included within Smeg's portable version.</p><h2 id="3-clean-up-is-easy">3. Clean up is easy</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3062px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="mieDQ7imd3U3MHiTeroGVF" name="Induction cooktop" alt="Hisense Hi8 induction cooktop" src="https://cdn.mos.cms.futurecdn.net/mieDQ7imd3U3MHiTeroGVF.jpg" mos="" align="middle" fullscreen="" width="3062" height="1722" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I can forget about spending time after I’ve enjoyed a meal cleaning up my cooktop. The flat surface makes it easy to wipe over. There are no awkward surfaces to work around, and with precision control, there’s less chance of spills and burnt-on messes.</p><p>What’s more, the Hisense Hi8 has a matt finish, so unlike the majority of induction cooktops with shiny surfaces, fingerprint issues are also obsolete.</p><h3 class="article-body__section" id="section-1-thing-to-know"><span>1 thing to know</span></h3><h2 id="you-may-need-to-change-your-pans">You may need to change your pans</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3000px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="RhPPP4z4s47XPFwfwQRZR6" name="Induction cooktop-icon" alt="Induction cooktop with icon in background" src="https://cdn.mos.cms.futurecdn.net/RhPPP4z4s47XPFwfwQRZR6.jpg" mos="" align="middle" fullscreen="" width="3000" height="1687" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>I’d been using my previous pans for over 20 years, and they still have plenty of life left in them. However, as they are not induction-friendly, they simply won’t work.</p><p>Induction cooking works by passing an electric current through a coiled copper wire beneath the cooking surface. This creates magnetic energy that interacts with the cookware rather than the cooktop’s surface. For heat to be transferred, your pans need to be magnetic. That means using <a href="https://www.tomsguide.com/home/can-you-use-stainless-steel-pans-on-an-induction-cooktop-heres-what-the-experts-say">stainless steel</a> or <a href="https://www.tomsguide.com/home/does-an-induction-cooktop-work-with-cast-iron-pans-heres-what-the-experts-say">cast iron</a>. Aluminum and copper pans won’t work.</p><p>Today, this isn’t a big issue, as when I was shopping for my new pans, the majority were induction-compatible. But if you’re not sure what to buy, look out for a symbol resembling a coil. And if you're <em>still</em> not sure what to look for, drop me a question in the comments below and I'll be happy to answer.</p><div class="product"><a data-dimension112="004568aa-9d71-11f1-9fc2-77e131a793c7" data-action="Deal Block" data-label="Cookware, set of 6, stainless steel" data-dimension48="Cookware, set of 6, stainless steel" data-dimension25="$59" href="https://www.ikea.com/us/en/p/ikea-365-cookware-set-of-6-stainless-steel-70577908/?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:507px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="v5xMobVMWFvaSFQeBsxnmK" name="IKEA 365+ Cookware, set of 6, stainless steel pans" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/v5xMobVMWFvaSFQeBsxnmK.png" mos="" align="middle" fullscreen="" width="507" height="507" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/ikea-365-cookware-set-of-6-stainless-steel-70577908/?" target="_blank" rel="nofollow" data-dimension112="004568aa-9d71-11f1-9fc2-77e131a793c7" data-action="Deal Block" data-label="Cookware, set of 6, stainless steel" data-dimension48="Cookware, set of 6, stainless steel" data-dimension25="$59">Cookware, set of 6, stainless steel: $59</a></strong><br>IKEA's cookware set is made of stainless steel and is compatible with all cooktops, including induction. The three pans come with a lid and are dishwasher- and oven-safe. <a class="view-deal button" href="https://www.ikea.com/us/en/p/ikea-365-cookware-set-of-6-stainless-steel-70577908/?" target="_blank" rel="nofollow" data-dimension112="004568aa-9d71-11f1-9fc2-77e131a793c7" data-action="Deal Block" data-label="Cookware, set of 6, stainless steel" data-dimension48="Cookware, set of 6, stainless steel" data-dimension25="$59">View Deal</a></p></div><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/does-an-induction-cooktop-work-with-cast-iron-pans-heres-what-the-experts-say">Does an induction cooktop work with cast iron pans? Here’s what the experts say</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/i-switched-to-induction-cooking-heres-why-im-never-going-back">I switched to induction cooking in 2024 — here's why I'm never going back</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/are-you-damaging-your-induction-cooktop-heres-what-you-should-do-to-protect-it">Are you damaging your induction cooktop? Here’s what you should do to protect it</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ My kitchen is getting a makeover: 5 clever IKEA finds I'm buying from $4 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>There’s no bigger project in your home than renovating a kitchen. It’s a lengthy process even before the fitting starts, and it requires decision after decision.</p><p>But after having spent weeks washing up in the bathtub and wiping plaster dust off my <a href="https://www.tomsguide.com/best-picks/best-cutting-boards">cutting boards</a>, I’ve survived and have finally got to the fun part. I’m now ready to choose all the accessories for my brand-new kitchen. </p><p>But this major project has left a big dent in my bank balance, so I need to find <a href="https://www.tomsguide.com/home/12-ikea-finds-to-double-your-kitchen-space">budget-friendly ways to style and organize the space</a>. That’s why I’m heading to IKEA, and I’ll be putting these five items into my basket to help make my kitchen functional and fun.</p><h2 id="style-on-a-budget">Style on a budget</h2><div class="product"><a data-dimension112="b3b59798-9bd7-11f1-ba00-519fd54c3811" data-action="Deal Block" data-label="FÄRGKLAR Mug" data-dimension48="FÄRGKLAR Mug" data-dimension25="$4" href="https://www.ikea.com/us/en/p/faergklar-mug-light-pink-50604880/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:563px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="ni9MRt7B2CZvSHoP8TXqdG" name="FÄRGKLAR Mug" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ni9MRt7B2CZvSHoP8TXqdG.png" mos="" align="middle" fullscreen="" width="563" height="563" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/faergklar-mug-light-pink-50604880/" target="_blank" rel="nofollow" data-dimension112="b3b59798-9bd7-11f1-ba00-519fd54c3811" data-action="Deal Block" data-label="FÄRGKLAR Mug" data-dimension48="FÄRGKLAR Mug" data-dimension25="$4">FÄRGKLAR Mug: $4</a></strong><br>If you think pink kitchen cabinets are a step too far (I nearly did it!), you can still add a touch of pink to your room. I'll be keeping these light pink mugs on an open shelf in my kitchen, rather than behind a closed cabinet. What’s more, if you like the design, you can buy matching items, including an 18-piece dinner set for $69.<a class="view-deal button" href="https://www.ikea.com/us/en/p/faergklar-mug-light-pink-50604880/" target="_blank" rel="nofollow" data-dimension112="b3b59798-9bd7-11f1-ba00-519fd54c3811" data-action="Deal Block" data-label="FÄRGKLAR Mug" data-dimension48="FÄRGKLAR Mug" data-dimension25="$4">View Deal</a></p></div><div class="product"><a data-dimension112="ca866dda-9bd2-11f1-a802-7b3ac17989c3" data-action="Deal Block" data-label="KLIPPKAKTUS Lazy susan" data-dimension48="KLIPPKAKTUS Lazy susan" data-dimension25="$11" href="https://www.ikea.com/us/en/p/klippkaktus-lazy-susan-00569426/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1400px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="mQhN9p9FceZ7C6RQsnVQCT" name="IKEA Klippkaktus lazy susan" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/mQhN9p9FceZ7C6RQsnVQCT.png" mos="" align="middle" fullscreen="" width="1400" height="1400" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/klippkaktus-lazy-susan-00569426/" target="_blank" rel="nofollow" data-dimension112="ca866dda-9bd2-11f1-a802-7b3ac17989c3" data-action="Deal Block" data-label="KLIPPKAKTUS Lazy susan" data-dimension48="KLIPPKAKTUS Lazy susan" data-dimension25="$11">KLIPPKAKTUS Lazy susan: $11</a></strong><br>Fed up with scrambling through your refrigerator or cabinets to find that sauce? A Lazy Susan is the perfect solution for easy storage. The spinning turntable rotates to make it easier to grab that jar of pickles that’s stuck at the back. I'm going to add IKEA's version to my refrigerator, but this clever gadget works equally well in other kitchen storage scenarios. <a class="view-deal button" href="https://www.ikea.com/us/en/p/klippkaktus-lazy-susan-00569426/" target="_blank" rel="nofollow" data-dimension112="ca866dda-9bd2-11f1-a802-7b3ac17989c3" data-action="Deal Block" data-label="KLIPPKAKTUS Lazy susan" data-dimension48="KLIPPKAKTUS Lazy susan" data-dimension25="$11">View Deal</a></p></div><div class="product"><a data-dimension112="a6f4aa68-9bd6-11f1-b23a-355f4e0cbedf" data-action="Deal Block" data-label="SPJUTLILJA Spice mill, set of 2" data-dimension48="SPJUTLILJA Spice mill, set of 2" data-dimension25="$14.99" href="https://www.ikea.com/us/en/p/spjutlilja-spice-mill-set-of-2-black-light-beige-10624229/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="xdYGmFofjNBztzpPRjFCyk" name="IKEA SPJUTLILJA Spice mill, set of 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/xdYGmFofjNBztzpPRjFCyk.png" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/spjutlilja-spice-mill-set-of-2-black-light-beige-10624229/" target="_blank" rel="nofollow" data-dimension112="a6f4aa68-9bd6-11f1-b23a-355f4e0cbedf" data-action="Deal Block" data-label="SPJUTLILJA Spice mill, set of 2" data-dimension48="SPJUTLILJA Spice mill, set of 2" data-dimension25="$14.99">SPJUTLILJA Spice mill, set of 2: $14.99</a></strong><br>I love grinding fresh peppercorns over my meals, but I daren't put my current grinder on the table. So, when I spotted this striking salt-and-pepper milling set at IKEA, it had to go into my basket. Who said functional couldn’t be fun? The striking vertical black-and-white design will make a statement on your countertop, and at under $15, I think it's one of the reasons people love shopping at IKEA. <a class="view-deal button" href="https://www.ikea.com/us/en/p/spjutlilja-spice-mill-set-of-2-black-light-beige-10624229/" target="_blank" rel="nofollow" data-dimension112="a6f4aa68-9bd6-11f1-b23a-355f4e0cbedf" data-action="Deal Block" data-label="SPJUTLILJA Spice mill, set of 2" data-dimension48="SPJUTLILJA Spice mill, set of 2" data-dimension25="$14.99">View Deal</a></p></div><div class="product"><a data-dimension112="05e2813c-9bd5-11f1-ad4c-b7d72814a22e" data-action="Deal Block" data-label="APTITLIG Butcher block" data-dimension48="APTITLIG Butcher block" data-dimension25="$24.99" href="https://www.ikea.com/us/en/p/aptitlig-butcher-block-bamboo-00233429/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1400px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="eBJwY3KbwK6M9H36wzWUgX" name="IKEA Aptitlig butcher block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/eBJwY3KbwK6M9H36wzWUgX.png" mos="" align="middle" fullscreen="" width="1400" height="1400" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/aptitlig-butcher-block-bamboo-00233429/" target="_blank" rel="nofollow" data-dimension112="05e2813c-9bd5-11f1-ad4c-b7d72814a22e" data-action="Deal Block" data-label="APTITLIG Butcher block" data-dimension48="APTITLIG Butcher block" data-dimension25="$24.99">APTITLIG Butcher block: $24.99</a></strong><br>I never manage with small chopping boards, so this large butcher's block is perfect for chopping large quantities of fruits and vegetables. The bamboo board measures 17 ¾ x 14 ¼  x 1 ¼," and has a groove running around the edge to stop juices from spilling onto your countertop. To keep mine looking pristine, I’ll be coating it with food-safe mineral oil to extend its life.<a class="view-deal button" href="https://www.ikea.com/us/en/p/aptitlig-butcher-block-bamboo-00233429/" target="_blank" rel="nofollow" data-dimension112="05e2813c-9bd5-11f1-ad4c-b7d72814a22e" data-action="Deal Block" data-label="APTITLIG Butcher block" data-dimension48="APTITLIG Butcher block" data-dimension25="$24.99">View Deal</a></p></div><div class="product"><a data-dimension112="46d5b5f4-9bdc-11f1-acc3-f1a29d5bafb3" data-action="Deal Block" data-label="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension48="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension25="$29.99" href="https://www.ikea.com/us/en/p/klippfisk-frying-pan-non-stick-coating-wood-dark-gray-blue-90602501/#content" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="wnYbZjdBraNtr4x8riKz69" name="IKEA KLIPPFISK frying pan" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/wnYbZjdBraNtr4x8riKz69.png" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/klippfisk-frying-pan-non-stick-coating-wood-dark-gray-blue-90602501/#content" target="_blank" rel="nofollow" data-dimension112="46d5b5f4-9bdc-11f1-acc3-f1a29d5bafb3" data-action="Deal Block" data-label="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension48="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension25="$29.99">KLIPPFISK Frying pan, 9 1/2 inch: $29.99</a></strong><br>Having swapped my gas cooktop for an induction one, I’ve had to change all my pots and pans to induction-friendly ones. This modern frying pan in blue-grey cast aluminum has two spouts, a non-stick coating and a beautiful rubberwood handle, plus it's compatible with all cooktops. If you’d prefer a larger version, you can opt for the 11-inch pan.<a class="view-deal button" href="https://www.ikea.com/us/en/p/klippfisk-frying-pan-non-stick-coating-wood-dark-gray-blue-90602501/#content" target="_blank" rel="nofollow" data-dimension112="46d5b5f4-9bdc-11f1-acc3-f1a29d5bafb3" data-action="Deal Block" data-label="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension48="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension25="$29.99">View Deal</a></p></div><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchen-dining/you-dont-need-an-appliance-garage-ive-been-using-this-usd29-ikea-classic-for-years-and-it-keeps-my-gadgets-neatly-stowed-away">You don’t need an appliance garage — I’ve been using this $29 IKEA classic for years, and it keeps my gadgets neatly stowed away</a></li><li><a href="https://www.tomsguide.com/home/kitchen-dining/ikeas-stackable-food-storage-containers-just-doubled-my-kitchen-counter-space-and-they-only-cost-usd14">IKEA’s stackable food storage containers just doubled my kitchen counter space, and they only cost $14</a></li><li><a href="https://www.tomsguide.com/home/kitchen-dining/i-tested-ikeas-portable-induction-cooktop-and-its-a-game-changer-for-small-spaces">I tested IKEA's portable induction cooktop — and it's a game changer for small spaces</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/my-kitchen-is-getting-a-makeover-5-clever-ikea-finds-im-buying-from-usd4</link>
                                                                            <description>
                            <![CDATA[ I’m currently renovating my kitchen on a budget and here are 5 IKEA items I’m adding to my basket. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">uNcQNQMkjZNcFy2yspsAy7</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/qg5Z6rnAaTsHSyK9TWXmTN-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 22 Aug 2026 13:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ camilla.sharman@futurenet.com (Camilla Sharman) ]]></author>                    <dc:creator><![CDATA[ Camilla Sharman ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/nY4nvWzofHKHpvzAqN5LVH.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Camilla is the Homes Staff Writer and covers everything to do with homes and gardens. She has a wealth of editorial experience, mounting over 30 years, and covers news and features, tests products for reviews and compiles buying guides.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Her work has appeared in business and consumer titles, including Ideal Home, Real Homes, House Beautiful, Homebuilding &amp;amp; Renovation, and Kitchen &amp;amp; Bathroom Business. She’s even appeared on the cover of Your Home, writing about her own house renovation.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Although she’s obsessed with decorating her home, she also enjoys baking and trying out the latest kitchen appliances. But when she’s not inside, you’ll find her pottering about in her yard, tending to her vegetable patch or taking in her prized hydrangeas. She also enjoys keeping fit, and if she&#039;s not on a spin bike trying to keep up with the class, she&#039;ll be in the pool, or trying to perfect her headstand in a yoga class.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/qg5Z6rnAaTsHSyK9TWXmTN-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[An closeup exterior shot of an IKEA store.]]></media:description>                                                            <media:text><![CDATA[An closeup exterior shot of an IKEA store.]]></media:text>
                                <media:title type="plain"><![CDATA[An closeup exterior shot of an IKEA store.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/qg5Z6rnAaTsHSyK9TWXmTN-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>There’s no bigger project in your home than renovating a kitchen. It’s a lengthy process even before the fitting starts, and it requires decision after decision.</p><p>But after having spent weeks washing up in the bathtub and wiping plaster dust off my <a href="https://www.tomsguide.com/best-picks/best-cutting-boards">cutting boards</a>, I’ve survived and have finally got to the fun part. I’m now ready to choose all the accessories for my brand-new kitchen. </p><p>But this major project has left a big dent in my bank balance, so I need to find <a href="https://www.tomsguide.com/home/12-ikea-finds-to-double-your-kitchen-space">budget-friendly ways to style and organize the space</a>. That’s why I’m heading to IKEA, and I’ll be putting these five items into my basket to help make my kitchen functional and fun.</p><h2 id="style-on-a-budget">Style on a budget</h2><div class="product"><a data-dimension112="b3b59798-9bd7-11f1-ba00-519fd54c3811" data-action="Deal Block" data-label="FÄRGKLAR Mug" data-dimension48="FÄRGKLAR Mug" data-dimension25="$4" href="https://www.ikea.com/us/en/p/faergklar-mug-light-pink-50604880/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:563px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="ni9MRt7B2CZvSHoP8TXqdG" name="FÄRGKLAR Mug" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ni9MRt7B2CZvSHoP8TXqdG.png" mos="" align="middle" fullscreen="" width="563" height="563" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/faergklar-mug-light-pink-50604880/" target="_blank" rel="nofollow" data-dimension112="b3b59798-9bd7-11f1-ba00-519fd54c3811" data-action="Deal Block" data-label="FÄRGKLAR Mug" data-dimension48="FÄRGKLAR Mug" data-dimension25="$4">FÄRGKLAR Mug: $4</a></strong><br>If you think pink kitchen cabinets are a step too far (I nearly did it!), you can still add a touch of pink to your room. I'll be keeping these light pink mugs on an open shelf in my kitchen, rather than behind a closed cabinet. What’s more, if you like the design, you can buy matching items, including an 18-piece dinner set for $69.<a class="view-deal button" href="https://www.ikea.com/us/en/p/faergklar-mug-light-pink-50604880/" target="_blank" rel="nofollow" data-dimension112="b3b59798-9bd7-11f1-ba00-519fd54c3811" data-action="Deal Block" data-label="FÄRGKLAR Mug" data-dimension48="FÄRGKLAR Mug" data-dimension25="$4">View Deal</a></p></div><div class="product"><a data-dimension112="ca866dda-9bd2-11f1-a802-7b3ac17989c3" data-action="Deal Block" data-label="KLIPPKAKTUS Lazy susan" data-dimension48="KLIPPKAKTUS Lazy susan" data-dimension25="$11" href="https://www.ikea.com/us/en/p/klippkaktus-lazy-susan-00569426/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1400px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="mQhN9p9FceZ7C6RQsnVQCT" name="IKEA Klippkaktus lazy susan" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/mQhN9p9FceZ7C6RQsnVQCT.png" mos="" align="middle" fullscreen="" width="1400" height="1400" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/klippkaktus-lazy-susan-00569426/" target="_blank" rel="nofollow" data-dimension112="ca866dda-9bd2-11f1-a802-7b3ac17989c3" data-action="Deal Block" data-label="KLIPPKAKTUS Lazy susan" data-dimension48="KLIPPKAKTUS Lazy susan" data-dimension25="$11">KLIPPKAKTUS Lazy susan: $11</a></strong><br>Fed up with scrambling through your refrigerator or cabinets to find that sauce? A Lazy Susan is the perfect solution for easy storage. The spinning turntable rotates to make it easier to grab that jar of pickles that’s stuck at the back. I'm going to add IKEA's version to my refrigerator, but this clever gadget works equally well in other kitchen storage scenarios. <a class="view-deal button" href="https://www.ikea.com/us/en/p/klippkaktus-lazy-susan-00569426/" target="_blank" rel="nofollow" data-dimension112="ca866dda-9bd2-11f1-a802-7b3ac17989c3" data-action="Deal Block" data-label="KLIPPKAKTUS Lazy susan" data-dimension48="KLIPPKAKTUS Lazy susan" data-dimension25="$11">View Deal</a></p></div><div class="product"><a data-dimension112="a6f4aa68-9bd6-11f1-b23a-355f4e0cbedf" data-action="Deal Block" data-label="SPJUTLILJA Spice mill, set of 2" data-dimension48="SPJUTLILJA Spice mill, set of 2" data-dimension25="$14.99" href="https://www.ikea.com/us/en/p/spjutlilja-spice-mill-set-of-2-black-light-beige-10624229/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="xdYGmFofjNBztzpPRjFCyk" name="IKEA SPJUTLILJA Spice mill, set of 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/xdYGmFofjNBztzpPRjFCyk.png" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/spjutlilja-spice-mill-set-of-2-black-light-beige-10624229/" target="_blank" rel="nofollow" data-dimension112="a6f4aa68-9bd6-11f1-b23a-355f4e0cbedf" data-action="Deal Block" data-label="SPJUTLILJA Spice mill, set of 2" data-dimension48="SPJUTLILJA Spice mill, set of 2" data-dimension25="$14.99">SPJUTLILJA Spice mill, set of 2: $14.99</a></strong><br>I love grinding fresh peppercorns over my meals, but I daren't put my current grinder on the table. So, when I spotted this striking salt-and-pepper milling set at IKEA, it had to go into my basket. Who said functional couldn’t be fun? The striking vertical black-and-white design will make a statement on your countertop, and at under $15, I think it's one of the reasons people love shopping at IKEA. <a class="view-deal button" href="https://www.ikea.com/us/en/p/spjutlilja-spice-mill-set-of-2-black-light-beige-10624229/" target="_blank" rel="nofollow" data-dimension112="a6f4aa68-9bd6-11f1-b23a-355f4e0cbedf" data-action="Deal Block" data-label="SPJUTLILJA Spice mill, set of 2" data-dimension48="SPJUTLILJA Spice mill, set of 2" data-dimension25="$14.99">View Deal</a></p></div><div class="product"><a data-dimension112="05e2813c-9bd5-11f1-ad4c-b7d72814a22e" data-action="Deal Block" data-label="APTITLIG Butcher block" data-dimension48="APTITLIG Butcher block" data-dimension25="$24.99" href="https://www.ikea.com/us/en/p/aptitlig-butcher-block-bamboo-00233429/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1400px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="eBJwY3KbwK6M9H36wzWUgX" name="IKEA Aptitlig butcher block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/eBJwY3KbwK6M9H36wzWUgX.png" mos="" align="middle" fullscreen="" width="1400" height="1400" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/aptitlig-butcher-block-bamboo-00233429/" target="_blank" rel="nofollow" data-dimension112="05e2813c-9bd5-11f1-ad4c-b7d72814a22e" data-action="Deal Block" data-label="APTITLIG Butcher block" data-dimension48="APTITLIG Butcher block" data-dimension25="$24.99">APTITLIG Butcher block: $24.99</a></strong><br>I never manage with small chopping boards, so this large butcher's block is perfect for chopping large quantities of fruits and vegetables. The bamboo board measures 17 ¾ x 14 ¼  x 1 ¼," and has a groove running around the edge to stop juices from spilling onto your countertop. To keep mine looking pristine, I’ll be coating it with food-safe mineral oil to extend its life.<a class="view-deal button" href="https://www.ikea.com/us/en/p/aptitlig-butcher-block-bamboo-00233429/" target="_blank" rel="nofollow" data-dimension112="05e2813c-9bd5-11f1-ad4c-b7d72814a22e" data-action="Deal Block" data-label="APTITLIG Butcher block" data-dimension48="APTITLIG Butcher block" data-dimension25="$24.99">View Deal</a></p></div><div class="product"><a data-dimension112="46d5b5f4-9bdc-11f1-acc3-f1a29d5bafb3" data-action="Deal Block" data-label="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension48="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension25="$29.99" href="https://www.ikea.com/us/en/p/klippfisk-frying-pan-non-stick-coating-wood-dark-gray-blue-90602501/#content" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="wnYbZjdBraNtr4x8riKz69" name="IKEA KLIPPFISK frying pan" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/wnYbZjdBraNtr4x8riKz69.png" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.ikea.com/us/en/p/klippfisk-frying-pan-non-stick-coating-wood-dark-gray-blue-90602501/#content" target="_blank" rel="nofollow" data-dimension112="46d5b5f4-9bdc-11f1-acc3-f1a29d5bafb3" data-action="Deal Block" data-label="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension48="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension25="$29.99">KLIPPFISK Frying pan, 9 1/2 inch: $29.99</a></strong><br>Having swapped my gas cooktop for an induction one, I’ve had to change all my pots and pans to induction-friendly ones. This modern frying pan in blue-grey cast aluminum has two spouts, a non-stick coating and a beautiful rubberwood handle, plus it's compatible with all cooktops. If you’d prefer a larger version, you can opt for the 11-inch pan.<a class="view-deal button" href="https://www.ikea.com/us/en/p/klippfisk-frying-pan-non-stick-coating-wood-dark-gray-blue-90602501/#content" target="_blank" rel="nofollow" data-dimension112="46d5b5f4-9bdc-11f1-acc3-f1a29d5bafb3" data-action="Deal Block" data-label="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension48="KLIPPFISK Frying pan, 9 1/2 inch" data-dimension25="$29.99">View Deal</a></p></div><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchen-dining/you-dont-need-an-appliance-garage-ive-been-using-this-usd29-ikea-classic-for-years-and-it-keeps-my-gadgets-neatly-stowed-away">You don’t need an appliance garage — I’ve been using this $29 IKEA classic for years, and it keeps my gadgets neatly stowed away</a></li><li><a href="https://www.tomsguide.com/home/kitchen-dining/ikeas-stackable-food-storage-containers-just-doubled-my-kitchen-counter-space-and-they-only-cost-usd14">IKEA’s stackable food storage containers just doubled my kitchen counter space, and they only cost $14</a></li><li><a href="https://www.tomsguide.com/home/kitchen-dining/i-tested-ikeas-portable-induction-cooktop-and-its-a-game-changer-for-small-spaces">I tested IKEA's portable induction cooktop — and it's a game changer for small spaces</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The Nespresso machine coffee lovers rave about is back down to just AU$88 right now ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Mastering the perfect cup of coffee can be tricky, especially if you're a total newbie to the world of espresso machines. That's where the <a href="https://www.tomsguide.com/home/coffee-makers/ive-reviewed-countless-espresso-machines-this-year-but-theres-only-one-you-need-to-buy">best fully automatic coffee makers</a> come in, and one brand that simply excels in this category is Nespresso. </p><p>If pulling the perfect shot or calculating the grind size yourself is simply too much hassle, then a machine like Nespresso's Vertuo Pop could be the right fit for you. By removing the complexities of coffee bean selection and the excessive cleaning that comes with owning a manual machine, Nespresso's capsule system takes the guesswork out of barista-made cups at home, providing consistent cups with the simple touch of a button. </p><p>And right now, Nespresso's Vertuo Pop machine is down to just AU$88 — that's 62% off RRP. This price is exclusive to the <a href="https://www.amazon.com.au/DeLonghi-Nespresso-Single-Serve-Centrifusion-Technology/dp/B0BSV6MRM6" target="_blank">Pacific Blue</a>, <a href="https://www.amazon.com.au/dp/B0GMGFK12S" target="_blank">Lilac</a>, <a href="https://www.amazon.com.au/dp/B0BYSKJBYL" target="_blank">Coconut White</a> and <a href="https://www.amazon.com.au/Nespresso-DeLonghi-centrifusion-technology-Liquorice/dp/B0BSV5WCZV" target="_blank">Liquorice Black</a> colourways on Amazon. You'll also need to act fast to score this stunning coffee machine, though — this deal’s only available while stocks last.</p><div class="product"><a data-dimension112="8f2cd3fa-9d03-11f1-b7cf-cf3dd5cd0c03" data-action="Deal Block" data-label="Vertuo Pop" data-dimension48="Vertuo Pop" data-dimension25="$88" href="https://www.amazon.com.au/DeLonghi-Nespresso-Single-Serve-Centrifusion-Technology/dp/B0BSV6MRM6" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="H4VttYx8gH79ubjYe8yC2j" name="delonghi-nespresso-vertuo-pop-env90a-cap-aa4d1ae7-d0ce-41e0-b2a4-3a92ab59ff06.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/H4VttYx8gH79ubjYe8yC2j.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Now 62% off, the Nespresso Vertuo Pop machine is hard to pass up at an incredible AU$88. With one-touch convenience, you can brew up to four different cup sizes from a range of great Nespresso coffee pods. You'll also receive a 12-pack of coffee capsule samples to try. Available in <a href="https://www.amazon.com.au/DeLonghi-Nespresso-Single-Serve-Centrifusion-Technology/dp/B0BSV6MRM6" target="_blank" data-dimension112="8f2cd3fa-9d03-11f1-b7cf-cf3dd5cd0c03" data-action="Deal Block" data-label="Vertuo Pop" data-dimension48="Vertuo Pop" data-dimension25="$88">Pacific Blue</a>, <a href="https://www.amazon.com.au/dp/B0GMGFK12S" target="_blank">Lilac</a>, <a href="https://www.amazon.com.au/dp/B0BYSKJBYL" target="_blank">Coconut White</a> and <a href="https://www.amazon.com.au/Nespresso-DeLonghi-centrifusion-technology-Liquorice/dp/B0BSV5WCZV" target="_blank">Liquorice Black</a> colourways.<a class="view-deal button" href="https://www.amazon.com.au/DeLonghi-Nespresso-Single-Serve-Centrifusion-Technology/dp/B0BSV6MRM6" target="_blank" rel="nofollow" data-dimension112="8f2cd3fa-9d03-11f1-b7cf-cf3dd5cd0c03" data-action="Deal Block" data-label="Vertuo Pop" data-dimension48="Vertuo Pop" data-dimension25="$88">View Deal</a></p></div><p>It's worth noting that this machine is rather fuss-free, and it's as simple as plugging it in, filling the water tank and hitting the button before sipping on your first brew. More often than not though, standalone Nespresso machines come with a sample pack of pods to try, but they don't always include a milk frother or extras to get your perfect sip sorted. </p><p>That said, plenty of Nespresso Vertuo models are sold in bundles, which include Nespresso's Aeroccino3 milk frother and, in some cases, a Nespresso-branded mug or tumbler. </p><p>If your budget can stretch to AU$179, you might want to consider nabbing the <a href="https://www.amazon.com.au/Nespresso-Single-Serve-Cappuccino-Centrifusion-Technology/dp/B0DXQ72F2X">Nespresso Vertuo Pop and the Aeroccino 3 milk frother</a>, with all the bells and whistles, in a fun Candy Pink bundle. This bundle also has the viral <a href="https://www.nespresso.com/au/en/order/accessories/original/iced-coffee-tumbler">Nespresso Travel Tumbler</a>, which typically retails at AU$42, and a fun canvas carry bag. </p><p>Not a fan of the Vertuo Pop's design? You might like the latest Vertuo model, the Up, which is discounted to <a href="https://www.amazon.com.au/DeLonghi-Nespresso-Vertuo-Aeroccino-ENV200-WAE/dp/B0GQB1KPVB/">AU$299 in White or Grey colourways</a>. The Up also includes the Aeroccino milk frother and the sample pods. </p><p>I've popped both deals below if you're ready to upgrade your morning routine with this one-touch convenience. </p>        <div class="featured_product_block featured_block_hero" data-id="8f2cd4e0-9d03-11f1-aa1e-0535c0d3e846">            <a href="https://www.amazon.com.au/Nespresso-Single-Serve-Cappuccino-Centrifusion-Technology/dp/B0DXQ72F2X" data-model-name="Nespresso Vertuo Pop and Aeroccino 3 Welcome Set (Candy Pink)" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/QakBSCJBqXzjp68m9nSru4.jpg" alt="De'longhi Nespresso Vertuo Pop and Aeroccino 3 Env90.ae, Capsule Coffee Machine, Single-Serve Pod Coffee and Cappuccino Machine, 4 Cup Sizes, Centrifusion Technology, Welcome Set Included, Candy Pink"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nespresso</div>                                        <div class="featured__title">Nespresso Vertuo Pop and Aeroccino 3 Welcome Set (Candy Pink)</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8f2cd54e-9d03-11f1-87be-3b8258d94185">            <a href="https://www.amazon.com.au/DeLonghi-Nespresso-Vertuo-Aeroccino-ENV200-WAE/dp/B0GQB1KPVB/" data-model-name="Vertuo Up with Aeroccino milk frother" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/EQrMFEGQVxCHHRg6pZGbx4.jpg" alt="De'longhi Nespresso Vertuo Up – Capsule Coffee Machine With Aeroccino Milk Frother, 7 Cup Sizes, Fast Heat-Up, 1.4l Water Tank, Creations Mode, Compact Design, White (env200.wae)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nespresso</div>                                        <div class="featured__title">Vertuo Up with Aeroccino milk frother</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/the-nespresso-machine-coffee-lovers-rave-about-is-back-down-to-just-aud88-right-now</link>
                                                                            <description>
                            <![CDATA[ Need a new Nespresso? Consider the coveted Nespresso Vertuo Pop, now just AU$88 on Amazon. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">3JXh3cRehDKcvozHkNa5VT</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/k8gAfTMvyp3QTaVGqyqR5T-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 21 Aug 2026 03:23:35 +0000</pubDate>                                                                                                                                <updated>Fri, 21 Aug 2026 03:23:53 +0000</updated>
                                                                                                                                            <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ lucy.scotting@futurenet.com (Lucy Scotting) ]]></author>                    <dc:creator><![CDATA[ Lucy Scotting ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/UC9ncAYxkmJ5ipHEyX44ri.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Lucy Scotting is a staff writer for Tom’s Guide Australia, primarily covering lifestyle, streaming and internet-related news. Lucy started her career writing for HR and staffing industry publications, with articles covering emerging tech, business and finance.&lt;/p&gt;&lt;p&gt;In her spare time, Lucy can be found watching sci-fi movies, working on her dystopian fiction novel (in progress since 2017) or hanging out with her dog, Fletcher. If she’s not found doing any of the above, she’s likely on her next adventure to a new city, country, or continent. &lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/k8gAfTMvyp3QTaVGqyqR5T-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Nespresso Pop]]></media:description>                                                            <media:text><![CDATA[Nespresso Pop]]></media:text>
                                <media:title type="plain"><![CDATA[Nespresso Pop]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/k8gAfTMvyp3QTaVGqyqR5T-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Mastering the perfect cup of coffee can be tricky, especially if you're a total newbie to the world of espresso machines. That's where the <a href="https://www.tomsguide.com/home/coffee-makers/ive-reviewed-countless-espresso-machines-this-year-but-theres-only-one-you-need-to-buy">best fully automatic coffee makers</a> come in, and one brand that simply excels in this category is Nespresso. </p><p>If pulling the perfect shot or calculating the grind size yourself is simply too much hassle, then a machine like Nespresso's Vertuo Pop could be the right fit for you. By removing the complexities of coffee bean selection and the excessive cleaning that comes with owning a manual machine, Nespresso's capsule system takes the guesswork out of barista-made cups at home, providing consistent cups with the simple touch of a button. </p><p>And right now, Nespresso's Vertuo Pop machine is down to just AU$88 — that's 62% off RRP. This price is exclusive to the <a href="https://www.amazon.com.au/DeLonghi-Nespresso-Single-Serve-Centrifusion-Technology/dp/B0BSV6MRM6" target="_blank">Pacific Blue</a>, <a href="https://www.amazon.com.au/dp/B0GMGFK12S" target="_blank">Lilac</a>, <a href="https://www.amazon.com.au/dp/B0BYSKJBYL" target="_blank">Coconut White</a> and <a href="https://www.amazon.com.au/Nespresso-DeLonghi-centrifusion-technology-Liquorice/dp/B0BSV5WCZV" target="_blank">Liquorice Black</a> colourways on Amazon. You'll also need to act fast to score this stunning coffee machine, though — this deal’s only available while stocks last.</p><div class="product"><a data-dimension112="8f2cd3fa-9d03-11f1-b7cf-cf3dd5cd0c03" data-action="Deal Block" data-label="Vertuo Pop" data-dimension48="Vertuo Pop" data-dimension25="$88" href="https://www.amazon.com.au/DeLonghi-Nespresso-Single-Serve-Centrifusion-Technology/dp/B0BSV6MRM6" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="H4VttYx8gH79ubjYe8yC2j" name="delonghi-nespresso-vertuo-pop-env90a-cap-aa4d1ae7-d0ce-41e0-b2a4-3a92ab59ff06.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/H4VttYx8gH79ubjYe8yC2j.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Now 62% off, the Nespresso Vertuo Pop machine is hard to pass up at an incredible AU$88. With one-touch convenience, you can brew up to four different cup sizes from a range of great Nespresso coffee pods. You'll also receive a 12-pack of coffee capsule samples to try. Available in <a href="https://www.amazon.com.au/DeLonghi-Nespresso-Single-Serve-Centrifusion-Technology/dp/B0BSV6MRM6" target="_blank" data-dimension112="8f2cd3fa-9d03-11f1-b7cf-cf3dd5cd0c03" data-action="Deal Block" data-label="Vertuo Pop" data-dimension48="Vertuo Pop" data-dimension25="$88">Pacific Blue</a>, <a href="https://www.amazon.com.au/dp/B0GMGFK12S" target="_blank">Lilac</a>, <a href="https://www.amazon.com.au/dp/B0BYSKJBYL" target="_blank">Coconut White</a> and <a href="https://www.amazon.com.au/Nespresso-DeLonghi-centrifusion-technology-Liquorice/dp/B0BSV5WCZV" target="_blank">Liquorice Black</a> colourways.<a class="view-deal button" href="https://www.amazon.com.au/DeLonghi-Nespresso-Single-Serve-Centrifusion-Technology/dp/B0BSV6MRM6" target="_blank" rel="nofollow" data-dimension112="8f2cd3fa-9d03-11f1-b7cf-cf3dd5cd0c03" data-action="Deal Block" data-label="Vertuo Pop" data-dimension48="Vertuo Pop" data-dimension25="$88">View Deal</a></p></div><p>It's worth noting that this machine is rather fuss-free, and it's as simple as plugging it in, filling the water tank and hitting the button before sipping on your first brew. More often than not though, standalone Nespresso machines come with a sample pack of pods to try, but they don't always include a milk frother or extras to get your perfect sip sorted. </p><p>That said, plenty of Nespresso Vertuo models are sold in bundles, which include Nespresso's Aeroccino3 milk frother and, in some cases, a Nespresso-branded mug or tumbler. </p><p>If your budget can stretch to AU$179, you might want to consider nabbing the <a href="https://www.amazon.com.au/Nespresso-Single-Serve-Cappuccino-Centrifusion-Technology/dp/B0DXQ72F2X">Nespresso Vertuo Pop and the Aeroccino 3 milk frother</a>, with all the bells and whistles, in a fun Candy Pink bundle. This bundle also has the viral <a href="https://www.nespresso.com/au/en/order/accessories/original/iced-coffee-tumbler">Nespresso Travel Tumbler</a>, which typically retails at AU$42, and a fun canvas carry bag. </p><p>Not a fan of the Vertuo Pop's design? You might like the latest Vertuo model, the Up, which is discounted to <a href="https://www.amazon.com.au/DeLonghi-Nespresso-Vertuo-Aeroccino-ENV200-WAE/dp/B0GQB1KPVB/">AU$299 in White or Grey colourways</a>. The Up also includes the Aeroccino milk frother and the sample pods. </p><p>I've popped both deals below if you're ready to upgrade your morning routine with this one-touch convenience. </p>        <div class="featured_product_block featured_block_hero" data-id="8f2cd4e0-9d03-11f1-aa1e-0535c0d3e846">            <a href="https://www.amazon.com.au/Nespresso-Single-Serve-Cappuccino-Centrifusion-Technology/dp/B0DXQ72F2X" data-model-name="Nespresso Vertuo Pop and Aeroccino 3 Welcome Set (Candy Pink)" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/QakBSCJBqXzjp68m9nSru4.jpg" alt="De'longhi Nespresso Vertuo Pop and Aeroccino 3 Env90.ae, Capsule Coffee Machine, Single-Serve Pod Coffee and Cappuccino Machine, 4 Cup Sizes, Centrifusion Technology, Welcome Set Included, Candy Pink"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nespresso</div>                                        <div class="featured__title">Nespresso Vertuo Pop and Aeroccino 3 Welcome Set (Candy Pink)</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8f2cd54e-9d03-11f1-87be-3b8258d94185">            <a href="https://www.amazon.com.au/DeLonghi-Nespresso-Vertuo-Aeroccino-ENV200-WAE/dp/B0GQB1KPVB/" data-model-name="Vertuo Up with Aeroccino milk frother" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/EQrMFEGQVxCHHRg6pZGbx4.jpg" alt="De'longhi Nespresso Vertuo Up – Capsule Coffee Machine With Aeroccino Milk Frother, 7 Cup Sizes, Fast Heat-Up, 1.4l Water Tank, Creations Mode, Compact Design, White (env200.wae)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nespresso</div>                                        <div class="featured__title">Vertuo Up with Aeroccino milk frother</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Moccamaster vs Simply Good Coffee — which premium coffee machine makes a better brew? Here's what I think ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the twentieth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>Anyone who knows anything about coffee has heard of Moccamaster. The Moccamaster (specifically the <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">KBGV Select</a>) is the most famous drip machine in the world, and there's very little competition. Few brands can go head-to-head with Technivorm's industrial, hardy stalwart, arguably the <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers">best coffee maker</a> of all time.</p><p>Mostly unchanged since its 1969 release, the Moccamaster does what many other coffee brands wish their flagship could: it has the best reputation in coffee. Whether this is from the industry-leading after-purchase care or the indestructible machine itself, I'm not sure. But people seriously love this thing. It's irrefutably "cult". </p><p>So enter the zippy young upstart — <a href="https://www.tomsguide.com/home/coffee-makers/simply-good-coffee-plastic-free-brewer-review">Simply Good Coffee Plastic-Free Brewer.</a> This model launched at the end of last year and was rolled out in 2026. What's the Plastic-Free Brewer got that the Moccamaster hasn't then? Well, the clue's in the name — it's not about what it <em>has</em>, but what it <em>hasn't</em>. Simply Good Coffee uses zero plastic in the brewing process. </p><p>Sure, this is all well and good — but what about taste? Let's find out which one makes a better brew. </p><h2 id="moccamaster-kbgv-select-amp-simply-good-coffee-plastic-free-brewer-what-39-s-the-difference">Moccamaster KBGV Select & Simply Good Coffee Plastic-Free Brewer: What's the difference?</h2><div class="product"><a data-dimension112="fd9c53d6-958f-11f1-9d02-bd0b05d941b8" data-action="Deal Block" data-label="Plastic-Free Brewer" data-dimension48="Plastic-Free Brewer" data-dimension25="$479" href="https://www.amazon.com/dp/B0FLYM8986" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="kBwcCTJrsdvHBBcz4LZYMa" name="Plastic-Free Brewer" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kBwcCTJrsdvHBBcz4LZYMa.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Simply Good Plastic-Free Brewer measures 13.7 x 7.8 x 14.6 inches, uses a #4 cone, and can brew up to 8 cups of coffee at a time.<a class="view-deal button" href="https://www.amazon.com/dp/B0FLYM8986" target="_blank" rel="nofollow" data-dimension112="fd9c53d6-958f-11f1-9d02-bd0b05d941b8" data-action="Deal Block" data-label="Plastic-Free Brewer" data-dimension48="Plastic-Free Brewer" data-dimension25="$479">View Deal</a></p></div><p>While at their cores, the Moccamaster KBGV Select and the Simply Good Coffee Plastic-Free Brewers are both drip/filter coffee machines, there are a few key differences. </p><p>These differences reside in two main areas: design and accreditation. </p><p>Obviously, the SGC Plastic-Free Brewer is that: plastic-free (well, low plastic). No hot water touches plastic anywhere in the brewing process — but it does have medical-grade silicone tubing in the water lines. Even so, I don't class this as "plastic" as it's not the kind of polymer that leaches into water or breaks down over time. </p><p>The SGC has plastic on the exterior to prevent burns. Seriously, imagine if it were completely steel. You'd need to go to the ER if you accidentally brush up against it after brewing. </p><p>Now, the Moccamaster KBGV Select has a plastic filter basket and a plastic destratification tube in the carafe. I personally don't mind this plastic — Technivorm (which makes Moccamaster) is a Dutch company, and the EU has seriously strict regulations on plastic. It's high-quality, non-leaching, BPA-and PFA-free — all the adjectives.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5242px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="DgfM3CL3QY7sPZLVG2N8xP" name="moccamaster and SGC machines" alt="the moccamaster and simply good coffee plastic-free brewer side by side" src="https://cdn.mos.cms.futurecdn.net/DgfM3CL3QY7sPZLVG2N8xP.jpg" mos="" align="middle" fullscreen="" width="5242" height="2948" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>So what else separates them? The SCA (Specialty Coffee Association) accreditation. Now, I don't think this is the be-all and end-all. There are more important things than an SCA accreditation. Taste, feel, emotional connection, secret ghosts inhabiting your coffee maker — whatever floats your boat (stews your brew? No. That's terrible. A career in stand-up comedy is clearly not for me). </p><p>If you do care about SCA accreditations, as I know a lot of coffee lovers do, you'll be more inclined to go with the Moccamaster. The KBGV Select has been SCA-accredited since the latter's advent in 2017, but the Simply Good Coffee Plastic-Free Brewer isn't yet accredited. </p><h2 id="moccamaster-kbgv-select-amp-simply-good-coffee-plastic-free-brewer-so-how-does-it-taste">Moccamaster KBGV Select & Simply Good Coffee Plastic-Free Brewer: So how does it taste?</h2><div class="product"><a data-dimension112="fd9c546c-958f-11f1-aa8e-e9f94f358517" data-action="Deal Block" data-label="Moccamaster KBGV Select" data-dimension48="Moccamaster KBGV Select" data-dimension25="$369" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2297px;"><p class="vanilla-image-block" style="padding-top:108.84%;"><img id="ViCKNPMdH4jYKkGFaBFznF" name="moccamaster_sorbet_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ViCKNPMdH4jYKkGFaBFznF.jpg" mos="" align="middle" fullscreen="" width="2297" height="2500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="fd9c546c-958f-11f1-aa8e-e9f94f358517" data-action="Deal Block" data-label="Moccamaster KBGV Select" data-dimension48="Moccamaster KBGV Select" data-dimension25="$369">Moccamaster KBGV Select: $369</a></strong><br>There's a reason why the <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster</a> is so revered in the coffee world: it seriously is that good. It's super easy to use, SCA-accredited (for both full-batch and half-batch), and makes full-bodied-but-not-bitter coffee.  </p><p><strong>Warranty check:</strong> 5 years as standard and <em>lifetime repairs</em>.<a class="view-deal button" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="fd9c546c-958f-11f1-aa8e-e9f94f358517" data-action="Deal Block" data-label="Moccamaster KBGV Select" data-dimension48="Moccamaster KBGV Select" data-dimension25="$369">View Deal</a></p></div><p>The Moccamaster KBGV Select and Simply Good Coffee Plastic-Free Brewer both make a rich, comforting, delicious brew with zero silt or bitterness. I would have no qualms recommending either of these machines for serious coffee lovers or newcomers alike.</p><p>There are a few main differences in flavor, though. Take a look at these two photos. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4342px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="Lri4XF5esj9TcvyXhPfFcE" name="moccamaster vs simply good coffee" alt="moccamaster and simply good coffee plastic-free coffee maker coffee side by side" src="https://cdn.mos.cms.futurecdn.net/Lri4XF5esj9TcvyXhPfFcE.jpg" mos="" align="middle" fullscreen="" width="4342" height="2442" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Left: Moccamaster | Right: SGC </span><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>The SGC Plastic-Free Brewer has a "Bloom" function, whereas the Moccamaster does not. In simple terms, this function steeps the coffee in water before pushing more water through for a full brew. This allows the coffee to grow more complex and can bring out nuanced flavors in the beans. </p><p>As a result, the SGC flavor is a little more juicy and what I would describe as "coffee-snob" territory. The Moccamaster tastes divine, don't get me wrong, but it brews a cup I would associate with "I'm a real coffee drinker because I drink my coffee black" memes. </p><p>I know people take water distribution seriously, so here's a comparison shot of the Moccamaster and Simply Good Coffee Plastic-Free Brewer filter baskets post-brew. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/2PYZV6Mf64zFtbGEE8WAwn.jpg" alt="funnelling on drip coffee makers" /><figcaption>Moccamaster<small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Hd4eCZDAGuMT6sq8FbSRwn.jpg" alt="funnelling on drip coffee makers" /><figcaption>Simply Good Coffee<small role="credit">Erin Bashford</small></figcaption></figure></figure><p>Honestly, there's no better or worse here. Both machines brew a delicious cup. I would be happy with either of these in my apartment. The Moccamaster is pure, classic coffee, and the SCG is a little more pure, classic coffee wearing a beanie hat and a flannel. </p><p>I would choose the Moccamaster over the SCG purely for the lifetime repairs (and, well, because the SCG isn't actually sold in the U.K. yet), but you wouldn't be disappointed with either.</p><p>Which brew appeals to you? Moccamaster or SGC? Let me know in the comments and stay tuned for next Monday's The Coffee Lab, on TikTok, Instagram, and YouTube. </p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/overwhelmed-by-coffee-jargon-here-are-the-5-key-espresso-terms-you-need-to-know">Overwhelmed by coffee jargon? Here are the 5 key espresso terms you need to know</a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/im-an-ex-starbucks-barista-you-can-make-the-iced-brown-sugar-oatmilk-shaken-espresso-at-home-for-a-fraction-of-the-cost">I'm an ex-Starbucks barista — you can make the Iced Brown Sugar Oatmilk Shaken Espresso at home for a fraction of the cost</a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it">I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/moccamaster-vs-simply-good-coffee-which-premium-coffee-machine-makes-a-better-brew-heres-what-i-think</link>
                                                                            <description>
                            <![CDATA[ On the 20th episode of The Coffee Lab, I'm putting two drip machines head-to-head to see which comes out on top. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">85HH9v8CSFSeopCSVAKNoS</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/CG6SfkgJb6hQFNGVxxyvRP-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 17 Aug 2026 15:12:13 +0000</pubDate>                                                                                                                                <updated>Mon, 24 Aug 2026 09:55:29 +0000</updated>
                                                                                                                                            <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/CG6SfkgJb6hQFNGVxxyvRP-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[a side by side composition of the moccamaster kgbv select vs simply good coffee plastic free brewer]]></media:description>                                                            <media:text><![CDATA[a side by side composition of the moccamaster kgbv select vs simply good coffee plastic free brewer]]></media:text>
                                <media:title type="plain"><![CDATA[a side by side composition of the moccamaster kgbv select vs simply good coffee plastic free brewer]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/CG6SfkgJb6hQFNGVxxyvRP-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the twentieth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>Anyone who knows anything about coffee has heard of Moccamaster. The Moccamaster (specifically the <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">KBGV Select</a>) is the most famous drip machine in the world, and there's very little competition. Few brands can go head-to-head with Technivorm's industrial, hardy stalwart, arguably the <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers">best coffee maker</a> of all time.</p><p>Mostly unchanged since its 1969 release, the Moccamaster does what many other coffee brands wish their flagship could: it has the best reputation in coffee. Whether this is from the industry-leading after-purchase care or the indestructible machine itself, I'm not sure. But people seriously love this thing. It's irrefutably "cult". </p><p>So enter the zippy young upstart — <a href="https://www.tomsguide.com/home/coffee-makers/simply-good-coffee-plastic-free-brewer-review">Simply Good Coffee Plastic-Free Brewer.</a> This model launched at the end of last year and was rolled out in 2026. What's the Plastic-Free Brewer got that the Moccamaster hasn't then? Well, the clue's in the name — it's not about what it <em>has</em>, but what it <em>hasn't</em>. Simply Good Coffee uses zero plastic in the brewing process. </p><p>Sure, this is all well and good — but what about taste? Let's find out which one makes a better brew. </p><h2 id="moccamaster-kbgv-select-amp-simply-good-coffee-plastic-free-brewer-what-39-s-the-difference">Moccamaster KBGV Select & Simply Good Coffee Plastic-Free Brewer: What's the difference?</h2><div class="product"><a data-dimension112="fd9c53d6-958f-11f1-9d02-bd0b05d941b8" data-action="Deal Block" data-label="Plastic-Free Brewer" data-dimension48="Plastic-Free Brewer" data-dimension25="$479" href="https://www.amazon.com/dp/B0FLYM8986" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="kBwcCTJrsdvHBBcz4LZYMa" name="Plastic-Free Brewer" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kBwcCTJrsdvHBBcz4LZYMa.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Simply Good Plastic-Free Brewer measures 13.7 x 7.8 x 14.6 inches, uses a #4 cone, and can brew up to 8 cups of coffee at a time.<a class="view-deal button" href="https://www.amazon.com/dp/B0FLYM8986" target="_blank" rel="nofollow" data-dimension112="fd9c53d6-958f-11f1-9d02-bd0b05d941b8" data-action="Deal Block" data-label="Plastic-Free Brewer" data-dimension48="Plastic-Free Brewer" data-dimension25="$479">View Deal</a></p></div><p>While at their cores, the Moccamaster KBGV Select and the Simply Good Coffee Plastic-Free Brewers are both drip/filter coffee machines, there are a few key differences. </p><p>These differences reside in two main areas: design and accreditation. </p><p>Obviously, the SGC Plastic-Free Brewer is that: plastic-free (well, low plastic). No hot water touches plastic anywhere in the brewing process — but it does have medical-grade silicone tubing in the water lines. Even so, I don't class this as "plastic" as it's not the kind of polymer that leaches into water or breaks down over time. </p><p>The SGC has plastic on the exterior to prevent burns. Seriously, imagine if it were completely steel. You'd need to go to the ER if you accidentally brush up against it after brewing. </p><p>Now, the Moccamaster KBGV Select has a plastic filter basket and a plastic destratification tube in the carafe. I personally don't mind this plastic — Technivorm (which makes Moccamaster) is a Dutch company, and the EU has seriously strict regulations on plastic. It's high-quality, non-leaching, BPA-and PFA-free — all the adjectives.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5242px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="DgfM3CL3QY7sPZLVG2N8xP" name="moccamaster and SGC machines" alt="the moccamaster and simply good coffee plastic-free brewer side by side" src="https://cdn.mos.cms.futurecdn.net/DgfM3CL3QY7sPZLVG2N8xP.jpg" mos="" align="middle" fullscreen="" width="5242" height="2948" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>So what else separates them? The SCA (Specialty Coffee Association) accreditation. Now, I don't think this is the be-all and end-all. There are more important things than an SCA accreditation. Taste, feel, emotional connection, secret ghosts inhabiting your coffee maker — whatever floats your boat (stews your brew? No. That's terrible. A career in stand-up comedy is clearly not for me). </p><p>If you do care about SCA accreditations, as I know a lot of coffee lovers do, you'll be more inclined to go with the Moccamaster. The KBGV Select has been SCA-accredited since the latter's advent in 2017, but the Simply Good Coffee Plastic-Free Brewer isn't yet accredited. </p><h2 id="moccamaster-kbgv-select-amp-simply-good-coffee-plastic-free-brewer-so-how-does-it-taste">Moccamaster KBGV Select & Simply Good Coffee Plastic-Free Brewer: So how does it taste?</h2><div class="product"><a data-dimension112="fd9c546c-958f-11f1-aa8e-e9f94f358517" data-action="Deal Block" data-label="Moccamaster KBGV Select" data-dimension48="Moccamaster KBGV Select" data-dimension25="$369" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2297px;"><p class="vanilla-image-block" style="padding-top:108.84%;"><img id="ViCKNPMdH4jYKkGFaBFznF" name="moccamaster_sorbet_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ViCKNPMdH4jYKkGFaBFznF.jpg" mos="" align="middle" fullscreen="" width="2297" height="2500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="fd9c546c-958f-11f1-aa8e-e9f94f358517" data-action="Deal Block" data-label="Moccamaster KBGV Select" data-dimension48="Moccamaster KBGV Select" data-dimension25="$369">Moccamaster KBGV Select: $369</a></strong><br>There's a reason why the <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster</a> is so revered in the coffee world: it seriously is that good. It's super easy to use, SCA-accredited (for both full-batch and half-batch), and makes full-bodied-but-not-bitter coffee.  </p><p><strong>Warranty check:</strong> 5 years as standard and <em>lifetime repairs</em>.<a class="view-deal button" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="fd9c546c-958f-11f1-aa8e-e9f94f358517" data-action="Deal Block" data-label="Moccamaster KBGV Select" data-dimension48="Moccamaster KBGV Select" data-dimension25="$369">View Deal</a></p></div><p>The Moccamaster KBGV Select and Simply Good Coffee Plastic-Free Brewer both make a rich, comforting, delicious brew with zero silt or bitterness. I would have no qualms recommending either of these machines for serious coffee lovers or newcomers alike.</p><p>There are a few main differences in flavor, though. Take a look at these two photos. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4342px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="Lri4XF5esj9TcvyXhPfFcE" name="moccamaster vs simply good coffee" alt="moccamaster and simply good coffee plastic-free coffee maker coffee side by side" src="https://cdn.mos.cms.futurecdn.net/Lri4XF5esj9TcvyXhPfFcE.jpg" mos="" align="middle" fullscreen="" width="4342" height="2442" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Left: Moccamaster | Right: SGC </span><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>The SGC Plastic-Free Brewer has a "Bloom" function, whereas the Moccamaster does not. In simple terms, this function steeps the coffee in water before pushing more water through for a full brew. This allows the coffee to grow more complex and can bring out nuanced flavors in the beans. </p><p>As a result, the SGC flavor is a little more juicy and what I would describe as "coffee-snob" territory. The Moccamaster tastes divine, don't get me wrong, but it brews a cup I would associate with "I'm a real coffee drinker because I drink my coffee black" memes. </p><p>I know people take water distribution seriously, so here's a comparison shot of the Moccamaster and Simply Good Coffee Plastic-Free Brewer filter baskets post-brew. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/2PYZV6Mf64zFtbGEE8WAwn.jpg" alt="funnelling on drip coffee makers" /><figcaption>Moccamaster<small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Hd4eCZDAGuMT6sq8FbSRwn.jpg" alt="funnelling on drip coffee makers" /><figcaption>Simply Good Coffee<small role="credit">Erin Bashford</small></figcaption></figure></figure><p>Honestly, there's no better or worse here. Both machines brew a delicious cup. I would be happy with either of these in my apartment. The Moccamaster is pure, classic coffee, and the SCG is a little more pure, classic coffee wearing a beanie hat and a flannel. </p><p>I would choose the Moccamaster over the SCG purely for the lifetime repairs (and, well, because the SCG isn't actually sold in the U.K. yet), but you wouldn't be disappointed with either.</p><p>Which brew appeals to you? Moccamaster or SGC? Let me know in the comments and stay tuned for next Monday's The Coffee Lab, on TikTok, Instagram, and YouTube. </p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/overwhelmed-by-coffee-jargon-here-are-the-5-key-espresso-terms-you-need-to-know">Overwhelmed by coffee jargon? Here are the 5 key espresso terms you need to know</a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/im-an-ex-starbucks-barista-you-can-make-the-iced-brown-sugar-oatmilk-shaken-espresso-at-home-for-a-fraction-of-the-cost">I'm an ex-Starbucks barista — you can make the Iced Brown Sugar Oatmilk Shaken Espresso at home for a fraction of the cost</a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it">I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ This Nutribullet blender costs less than $100 and performs exceptionally well — as long as you can deal with some wind ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I’m a firm believer that having one of the <a href="https://www.tomsguide.com/best-picks/best-blenders">best blenders</a> is the easiest way to level up your cooking at home, and squeeze in some extra nutrients. That means you need a blender that can cope with even the toughest ingredients. The Nutribullet Power Core does just that. </p><p>The Power Core costs less than $100, yet provides incredibly impressive performance and is easy to use. The large capacity means you can work in large batches, and the maintenance of the blender is really simple too. </p><p>Unfortunately, it’s extremely loud, and the motor creates a lot of airflow in your workspace. The limited speed settings could also put some users off, but I think it’s more than enough for home use. To find out more, you can keep reading this full Nutribullet Power core review. </p><h2 class="article-body__section" id="section-nutribullet-power-core-review-specs"><span>Nutribullet Power Core review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/nutribullet-Smoothies-Full-Size-Countertop-NBF70100/dp/B0GL4XLTDQ" target="_blank" rel="nofollow">$99</a> / <a href="https://www.amazon.co.uk/nutribullet-Blender-Pitcher-Automatic-Smoothie/dp/B0H1R8NX9J" target="_blank" rel="nofollow">£99</a></p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>15.5 x 7.8 x 7.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>7.5 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>72 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Power</strong></p></td><td  ><p>1400W (U.S.) / 1200W (U.K.)</p></td></tr><tr><td class="firstcol " ><p><strong>Blending speeds</strong></p></td><td  ><p>3 and pulse</p></td></tr><tr><td class="firstcol " ><p><strong>Blending presets</strong></p></td><td  ><p>Smoothie</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-nutribullet-power-core-review-price-availability"><span>Nutribullet Power Core review: Price & availability</span></h2><p>The Nutribullet Power Core is available from <a href="https://www.amazon.com/nutribullet-Smoothies-Full-Size-Countertop-NBF70100/dp/B0GL4XLTDQ" target="_blank" rel="nofollow">Amazon U.S. for $99</a> and <a href="https://www.amazon.co.uk/nutribullet-Blender-Pitcher-Automatic-Smoothie/dp/B0H1R8NX9J" target="_blank" rel="nofollow">Amazon U.K. for £99</a>. This is an incredibly affordable blender, but that's not unheard of from Nutribullet. In fact, they make some of the most affordable high-performance blenders, including the <a href="https://www.tomsguide.com/home/home-appliances/nutribullet-smart-touch-blender-review">Nutribullet Smart Touch</a> for $99 and the <a href="https://www.tomsguide.com/home/home-appliances/nutribullet-pro-900W-blender-review">Nutribullet Pro 900W</a> for $84. </p><p>If you want the option of smaller capacity blends, look at the <a href="https://www.tomsguide.com/home/home-appliances/ninja-detect-power-pro-with-blendsense-review">Ninja Detect Power Pro with BlendSense</a> for $179, which, like the Power Core, holds 72 ounces but comes with a 24-ounce single-serve cup. </p><p>For more control over blend speeds, check out the <a href="https://www.tomsguide.com/home/home-appliances/tribest-backspin-blender-review">Tribest Backspin</a> for $499, which has 12 speeds, or the <a href="https://www.tomsguide.com/home/home-appliances/vitamix-ascent-x2-blender-review">Vitamix Ascent X2</a> for $549 with 10 variable speeds. In short, if you want more control, you need to pay more because you only get three speeds with the Nutribullet Power Core. That said, for everyday use, this is enough. </p><h2 class="article-body__section" id="section-nutribullet-power-core-review-design"><span>Nutribullet Power Core review: Design </span></h2><p>The Nutribullet Power Core is a blender that will look good in any kitchen. It only comes in matte black, so if color matching all your appliances is a dealbreaker for you, this may not be the one (unless everything you own is black). But for most people, the simplistic design will be a win. </p><p>The jug has measurements up the side, so I found it easy to measure my ingredients as I went. The lid also has a small measuring cap that can be removed to add more ingredients, such as seasonings, as you blend. This was great for making things like dips. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="kP3FLQZCqDVLaVwqyCLXzZ" name="Nutribullet Power Core" alt="Nutribullet Power Core photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/kP3FLQZCqDVLaVwqyCLXzZ.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Because of the large handle, the jug is very easy to hold and pour even when it’s full and heavy. Also, the spout makes pouring accurate, and I didn’t have any issues with spilled smoothies. </p><p>The blender uses a six-point stainless steel blade, and this was very effective even on tough ingredients like dates and seeds.</p><h3 id="controls-2">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CWworxgkKos3cgNgPQqg3a" name="Nutribullet Power Core" alt="Nutribullet Power Core photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/CWworxgkKos3cgNgPQqg3a.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Nutribullet Power Core is very easy to use. All of the button controls are at the front of the machine, and since it’s so cheap, it’s very basic. For me, this is great because I don’t think blending needs to be complicated. </p><p>The power button is in the middle, and to the left you’ll find a smoothie preset and a pulse button. To the right, there are three buttons for low, medium, and high-speed blending. </p><h2 class="article-body__section" id="section-nutribullet-power-core-review-blending-performance"><span>Nutribullet Power Core review: Blending performance</span></h2><p>To test the blending performance of the Nutribullet Power Core, I carried out my usual tests and made a smoothie, sorbet, and hummus. When blending, the noise level reached a high of 101 decibels. This is around the volume you can expect from a lawnmower, and according to <a href="https://www.hearusa.com/hearing-loss/causes/decibels/">Hearing Loss USA</a>, “anything above 85 decibels can damage the small hair cells in your ears and affect your hearing health.” </p><p>This is <em>very</em> loud, even for a large blender. In comparison, the Vitamix Ascent X2 reaches about 81 decibels. I would seriously recommend using a pair of <a href="https://www.tomsguide.com/audio/earbuds/these-earplugs-are-designed-for-concerts-but-ive-found-another-use-for-them-now-i-dont-leave-the-house-without-my-loops">Loop earplugs</a> if you’re using this blender often.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="4CHHm4o6isXADQUxbzekRa" name="Nutribullet Power Core" alt="Nutribullet Power Core photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/4CHHm4o6isXADQUxbzekRa.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>That’s not the only thing — the motor base also creates a lot of wind. If it wasn’t for the powerful suction cups keeping it stable on my countertop, I’m pretty sure this thing would have taken flight. It blew around containers and papers on my countertop, which was just plain annoying. </p><p>Also, since the jug is so big, it would have been great to have a tamper so ingredients could be directed toward the blades when it wasn’t as full. I ended up having to push the sides down with a spoon intermittently.</p><h3 id="smoothie">Smoothie</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Yx8BSDmT3RpN2MoKJuNWk9" name="Nutribullet Power Core" alt="Smoothie made using the Nutribullet Power Core" src="https://cdn.mos.cms.futurecdn.net/Yx8BSDmT3RpN2MoKJuNWk9.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I added a bag of berry smoothie mix, dates, sunflower seeds and oat milk to the blender and used the smoothie preset for blending. This did a great job of quickly dealing with frozen ingredients and mincing the sunflower seeds finely. </p><p>I was most impressed by how the blender handled the dates, as their naturally sticky texture often clogs blender blades and leaves chunks behind. There was some texture from the fruit seeds, but I was left with a smooth drink, and the blades didn’t clog. For $99, this is an extremely impressive blender.</p><h3 id="sorbet">Sorbet</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RiQarmCSAhDEpr4Bv77du9" name="Nutribullet Power Core" alt="Mango sorbet made using the Nutribullet Power Core" src="https://cdn.mos.cms.futurecdn.net/RiQarmCSAhDEpr4Bv77du9.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Next, I blended up a sorbet. This allowed me to see how the blender would perform with frozen ingredients without adding liquid. I added a bag of frozen mango that had been thawed for 10 minutes. I used the medium-speed setting, stopping to push the mango down from the sides intermittently. </p><p>Since this is a thicker consistency than the smoothie, I spent a lot of time getting the mixture off the side walls of the blender. The mixture was mostly smooth with a few rogue chunks of mango, but it was good enough for what I needed.</p><h3 id="hummus">Hummus</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="dxaPyVGwDLaBSespdhjwa9" name="Nutribullet Power Core" alt="Hummus made using the Nutribullet Power Core" src="https://cdn.mos.cms.futurecdn.net/dxaPyVGwDLaBSespdhjwa9.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Finally, I whipped up a quick hummus — and I mean <em>quick</em>. I added two cans of chickpeas, lemon juice and some seasonings to the blender and used the pulse setting as I slowly added extra virgin olive oil. This only took a couple of minutes.  </p><p>I was very impressed with the results. I had a mostly smooth dip with only a few whole chickpeas. For the price, I couldn’t ask for more.</p><h2 class="article-body__section" id="section-nutribullet-power-core-review-storage-maintenance"><span>Nutribullet Power Core review: Storage & maintenance</span></h2><p>Taking care of the Nutribullet Power Core couldn’t be simpler. Everything is dishwasher-safe apart from the motor base. This meant I could just pop the whole jug and lid into the dishwasher and give the motor base a quick wipe down with a cloth. To clean the jug by hand, use detergent and a soft sponge, but be very careful with the blades at the bottom. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="K6vubQHZCbZMFXRgnjznNa" name="Nutribullet Power Core" alt="Nutribullet Power Core photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/K6vubQHZCbZMFXRgnjznNa.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You need to be prepared for it to take up a lot of space on your countertop, or potentially a whole shelf in your cupboard. It measures 15.5 x 7.8 x 7.5 inches and weighs 7.5 pounds. The 72-ounce jug detaches from the motor base for cleaning and easier storage. </p><h2 class="article-body__section" id="section-nutribullet-power-core-review-verdict"><span>Nutribullet Power Core review: Verdict</span></h2><p>The Nutribullet Power Core is an incredibly affordable blender that delivers impressive performance for under $100. This easy-to-use blender made mixing up large batches of my favorite smoothies, sorbets, and dips an absolute breeze. It completely blended even the toughest ingredients like dates, seeds and frozen fruit, leaving no chunks behind. It’s a must if you need a big blender on a budget, and it’s dishwasher-safe, so very easy to maintain. </p><p>However, you’ll need to protect your hearing with some earplugs because it gets incredibly noisy. I’d argue it’s louder than should be possible for a blender. The motor also generates a lot of air, making the surrounding workspace insanely windy. </p><p>With only three speeds, some users may feel limited, but for casual blenders, this is more than enough. For reliable and impressive results on a budget, this blender is a great choice. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/nutribullet-power-core-review</link>
                                                                            <description>
                            <![CDATA[ The Nutribullet Power Core is an impressive blender that costs less than $100, but the loud noise and airflow could be a dealbreaker for some. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">mutaAbz7ednBaVZYNh4T9P</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/QQDRojUGqyx6LZJjVhLeBa-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 14 Aug 2026 15:08:37 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ ashley.thieme@futurenet.com (Ashley Thieme) ]]></author>                    <dc:creator><![CDATA[ Ashley Thieme ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3AWovHjApwuNrSGRS6WBcL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ashley Thieme is a staff writer on the Reviews team at Tom’s Guide where she tests out the latest tech so you can know what’s going to be worth your time and money. She has a master’s degree in Magazine Journalism and a bachelor’s degree in Journalism, Media and Sociology from Cardiff University. She has bylines in titles including Women’s Health UK, sharing the latest health and wellness news stories, and Virgin Radio UK, providing the latest entertainment news and working on celebrity interviews. She has experience reporting on a variety of topics including music, literature, motorsport, entertainment and health. In previously published work, she has reviewed live music events, books, and wellness products. She values the importance of tech enhancing your life rather than taking over, and as a music fanatic, she is always looking for the best way to listen to new music releases. Discovering the top audio equipment that enhances sound quality and provides optimum comfort is what Ashley does best. In her spare time, Ashley enjoys hitting her reading goals on Goodreads by getting into the latest novels with a cup of tea as well as getting out in the Welsh mountains for a good hike on the weekend.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/QQDRojUGqyx6LZJjVhLeBa-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Nutribullet Power Core photographed in front of a blue background]]></media:description>                                                            <media:text><![CDATA[Nutribullet Power Core photographed in front of a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[Nutribullet Power Core photographed in front of a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/QQDRojUGqyx6LZJjVhLeBa-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I’m a firm believer that having one of the <a href="https://www.tomsguide.com/best-picks/best-blenders">best blenders</a> is the easiest way to level up your cooking at home, and squeeze in some extra nutrients. That means you need a blender that can cope with even the toughest ingredients. The Nutribullet Power Core does just that. </p><p>The Power Core costs less than $100, yet provides incredibly impressive performance and is easy to use. The large capacity means you can work in large batches, and the maintenance of the blender is really simple too. </p><p>Unfortunately, it’s extremely loud, and the motor creates a lot of airflow in your workspace. The limited speed settings could also put some users off, but I think it’s more than enough for home use. To find out more, you can keep reading this full Nutribullet Power core review. </p><h2 class="article-body__section" id="section-nutribullet-power-core-review-specs"><span>Nutribullet Power Core review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/nutribullet-Smoothies-Full-Size-Countertop-NBF70100/dp/B0GL4XLTDQ" target="_blank" rel="nofollow">$99</a> / <a href="https://www.amazon.co.uk/nutribullet-Blender-Pitcher-Automatic-Smoothie/dp/B0H1R8NX9J" target="_blank" rel="nofollow">£99</a></p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>15.5 x 7.8 x 7.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>7.5 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>72 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Power</strong></p></td><td  ><p>1400W (U.S.) / 1200W (U.K.)</p></td></tr><tr><td class="firstcol " ><p><strong>Blending speeds</strong></p></td><td  ><p>3 and pulse</p></td></tr><tr><td class="firstcol " ><p><strong>Blending presets</strong></p></td><td  ><p>Smoothie</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-nutribullet-power-core-review-price-availability"><span>Nutribullet Power Core review: Price & availability</span></h2><p>The Nutribullet Power Core is available from <a href="https://www.amazon.com/nutribullet-Smoothies-Full-Size-Countertop-NBF70100/dp/B0GL4XLTDQ" target="_blank" rel="nofollow">Amazon U.S. for $99</a> and <a href="https://www.amazon.co.uk/nutribullet-Blender-Pitcher-Automatic-Smoothie/dp/B0H1R8NX9J" target="_blank" rel="nofollow">Amazon U.K. for £99</a>. This is an incredibly affordable blender, but that's not unheard of from Nutribullet. In fact, they make some of the most affordable high-performance blenders, including the <a href="https://www.tomsguide.com/home/home-appliances/nutribullet-smart-touch-blender-review">Nutribullet Smart Touch</a> for $99 and the <a href="https://www.tomsguide.com/home/home-appliances/nutribullet-pro-900W-blender-review">Nutribullet Pro 900W</a> for $84. </p><p>If you want the option of smaller capacity blends, look at the <a href="https://www.tomsguide.com/home/home-appliances/ninja-detect-power-pro-with-blendsense-review">Ninja Detect Power Pro with BlendSense</a> for $179, which, like the Power Core, holds 72 ounces but comes with a 24-ounce single-serve cup. </p><p>For more control over blend speeds, check out the <a href="https://www.tomsguide.com/home/home-appliances/tribest-backspin-blender-review">Tribest Backspin</a> for $499, which has 12 speeds, or the <a href="https://www.tomsguide.com/home/home-appliances/vitamix-ascent-x2-blender-review">Vitamix Ascent X2</a> for $549 with 10 variable speeds. In short, if you want more control, you need to pay more because you only get three speeds with the Nutribullet Power Core. That said, for everyday use, this is enough. </p><h2 class="article-body__section" id="section-nutribullet-power-core-review-design"><span>Nutribullet Power Core review: Design </span></h2><p>The Nutribullet Power Core is a blender that will look good in any kitchen. It only comes in matte black, so if color matching all your appliances is a dealbreaker for you, this may not be the one (unless everything you own is black). But for most people, the simplistic design will be a win. </p><p>The jug has measurements up the side, so I found it easy to measure my ingredients as I went. The lid also has a small measuring cap that can be removed to add more ingredients, such as seasonings, as you blend. This was great for making things like dips. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="kP3FLQZCqDVLaVwqyCLXzZ" name="Nutribullet Power Core" alt="Nutribullet Power Core photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/kP3FLQZCqDVLaVwqyCLXzZ.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Because of the large handle, the jug is very easy to hold and pour even when it’s full and heavy. Also, the spout makes pouring accurate, and I didn’t have any issues with spilled smoothies. </p><p>The blender uses a six-point stainless steel blade, and this was very effective even on tough ingredients like dates and seeds.</p><h3 id="controls-2">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CWworxgkKos3cgNgPQqg3a" name="Nutribullet Power Core" alt="Nutribullet Power Core photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/CWworxgkKos3cgNgPQqg3a.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Nutribullet Power Core is very easy to use. All of the button controls are at the front of the machine, and since it’s so cheap, it’s very basic. For me, this is great because I don’t think blending needs to be complicated. </p><p>The power button is in the middle, and to the left you’ll find a smoothie preset and a pulse button. To the right, there are three buttons for low, medium, and high-speed blending. </p><h2 class="article-body__section" id="section-nutribullet-power-core-review-blending-performance"><span>Nutribullet Power Core review: Blending performance</span></h2><p>To test the blending performance of the Nutribullet Power Core, I carried out my usual tests and made a smoothie, sorbet, and hummus. When blending, the noise level reached a high of 101 decibels. This is around the volume you can expect from a lawnmower, and according to <a href="https://www.hearusa.com/hearing-loss/causes/decibels/">Hearing Loss USA</a>, “anything above 85 decibels can damage the small hair cells in your ears and affect your hearing health.” </p><p>This is <em>very</em> loud, even for a large blender. In comparison, the Vitamix Ascent X2 reaches about 81 decibels. I would seriously recommend using a pair of <a href="https://www.tomsguide.com/audio/earbuds/these-earplugs-are-designed-for-concerts-but-ive-found-another-use-for-them-now-i-dont-leave-the-house-without-my-loops">Loop earplugs</a> if you’re using this blender often.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="4CHHm4o6isXADQUxbzekRa" name="Nutribullet Power Core" alt="Nutribullet Power Core photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/4CHHm4o6isXADQUxbzekRa.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>That’s not the only thing — the motor base also creates a lot of wind. If it wasn’t for the powerful suction cups keeping it stable on my countertop, I’m pretty sure this thing would have taken flight. It blew around containers and papers on my countertop, which was just plain annoying. </p><p>Also, since the jug is so big, it would have been great to have a tamper so ingredients could be directed toward the blades when it wasn’t as full. I ended up having to push the sides down with a spoon intermittently.</p><h3 id="smoothie">Smoothie</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Yx8BSDmT3RpN2MoKJuNWk9" name="Nutribullet Power Core" alt="Smoothie made using the Nutribullet Power Core" src="https://cdn.mos.cms.futurecdn.net/Yx8BSDmT3RpN2MoKJuNWk9.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I added a bag of berry smoothie mix, dates, sunflower seeds and oat milk to the blender and used the smoothie preset for blending. This did a great job of quickly dealing with frozen ingredients and mincing the sunflower seeds finely. </p><p>I was most impressed by how the blender handled the dates, as their naturally sticky texture often clogs blender blades and leaves chunks behind. There was some texture from the fruit seeds, but I was left with a smooth drink, and the blades didn’t clog. For $99, this is an extremely impressive blender.</p><h3 id="sorbet">Sorbet</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RiQarmCSAhDEpr4Bv77du9" name="Nutribullet Power Core" alt="Mango sorbet made using the Nutribullet Power Core" src="https://cdn.mos.cms.futurecdn.net/RiQarmCSAhDEpr4Bv77du9.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Next, I blended up a sorbet. This allowed me to see how the blender would perform with frozen ingredients without adding liquid. I added a bag of frozen mango that had been thawed for 10 minutes. I used the medium-speed setting, stopping to push the mango down from the sides intermittently. </p><p>Since this is a thicker consistency than the smoothie, I spent a lot of time getting the mixture off the side walls of the blender. The mixture was mostly smooth with a few rogue chunks of mango, but it was good enough for what I needed.</p><h3 id="hummus">Hummus</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="dxaPyVGwDLaBSespdhjwa9" name="Nutribullet Power Core" alt="Hummus made using the Nutribullet Power Core" src="https://cdn.mos.cms.futurecdn.net/dxaPyVGwDLaBSespdhjwa9.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Finally, I whipped up a quick hummus — and I mean <em>quick</em>. I added two cans of chickpeas, lemon juice and some seasonings to the blender and used the pulse setting as I slowly added extra virgin olive oil. This only took a couple of minutes.  </p><p>I was very impressed with the results. I had a mostly smooth dip with only a few whole chickpeas. For the price, I couldn’t ask for more.</p><h2 class="article-body__section" id="section-nutribullet-power-core-review-storage-maintenance"><span>Nutribullet Power Core review: Storage & maintenance</span></h2><p>Taking care of the Nutribullet Power Core couldn’t be simpler. Everything is dishwasher-safe apart from the motor base. This meant I could just pop the whole jug and lid into the dishwasher and give the motor base a quick wipe down with a cloth. To clean the jug by hand, use detergent and a soft sponge, but be very careful with the blades at the bottom. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="K6vubQHZCbZMFXRgnjznNa" name="Nutribullet Power Core" alt="Nutribullet Power Core photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/K6vubQHZCbZMFXRgnjznNa.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You need to be prepared for it to take up a lot of space on your countertop, or potentially a whole shelf in your cupboard. It measures 15.5 x 7.8 x 7.5 inches and weighs 7.5 pounds. The 72-ounce jug detaches from the motor base for cleaning and easier storage. </p><h2 class="article-body__section" id="section-nutribullet-power-core-review-verdict"><span>Nutribullet Power Core review: Verdict</span></h2><p>The Nutribullet Power Core is an incredibly affordable blender that delivers impressive performance for under $100. This easy-to-use blender made mixing up large batches of my favorite smoothies, sorbets, and dips an absolute breeze. It completely blended even the toughest ingredients like dates, seeds and frozen fruit, leaving no chunks behind. It’s a must if you need a big blender on a budget, and it’s dishwasher-safe, so very easy to maintain. </p><p>However, you’ll need to protect your hearing with some earplugs because it gets incredibly noisy. I’d argue it’s louder than should be possible for a blender. The motor also generates a lot of air, making the surrounding workspace insanely windy. </p><p>With only three speeds, some users may feel limited, but for casual blenders, this is more than enough. For reliable and impressive results on a budget, this blender is a great choice. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Don't let summer end — Ninja pizza ovens, blenders and coolers are up to 42% off at Amazon ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Amazon is closing out the summer with a bunch of Ninja appliance deals. Whether you're hoping to extend grilling season into the fall, want to keep enjoying delicious smoothies, or even improve your knife skills while cooking — there are some discounts here that you definitely don't want to miss.</p><p>I've been searching the sale and have found <a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow">up to 30% off Ninja appliances</a>. I've listed all my favorites below, so keep scrolling to check them out.</p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow"><strong>shop all Ninja deals at Amazon</strong></a></li><li><strong>Ninja Blast Max: </strong><a href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow"><strong>was $99 now $69</strong></a><strong></strong></li><li><strong>Ninja Iced Coffee Maker: </strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0C25Y15TL" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Ninja Crispi 4-in-1 Portable Glass Air Fryer: </strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><strong>was $179 now $159</strong></a></li><li><strong>Ninja 10-Pc German Steel Knife Set: </strong><a href="https://www.amazon.com/dp/B0H2NLCZBQ" target="_blank" rel="nofollow"><strong>was $249 now $199</strong></a></li><li><strong>Ninja Woodfire Oven & Smoker: </strong><a href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow"><strong>was $399 now $279</strong></a></li><li><strong>Ninja Luxe Café Premier: </strong><a href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow"><strong>now $599</strong></a></li></ul><h3 class="article-body__section" id="section-ninja-deals"><span>Ninja Deals</span></h3><div class="product"><a data-dimension112="9ae6da84-96fe-11f1-a330-f9d854e50872" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69" href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1216px;"><p class="vanilla-image-block" style="padding-top:123.36%;"><img id="9Q4QY399FaozAFUt4j8dTW" name="ninja-blast-max-portable-blender--twist--bb517479-54ce-4a77-974c-58e2f44dde33.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/9Q4QY399FaozAFUt4j8dTW.jpg" mos="" align="middle" fullscreen="" width="1216" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Blast Max is capable of taking on any challenge you throw at it, including crushing ice and blitzing berries to make yummy smoothies. It comes with multiple blend settings to make sure your blender is using the right level of power for the task at hand, and the compact size means it’s a great option for anyone with a small kitchen. <a class="view-deal button" href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow" data-dimension112="9ae6da84-96fe-11f1-a330-f9d854e50872" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dae8-96fe-11f1-af0b-bffac6dd08f7" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79" href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1614px;"><p class="vanilla-image-block" style="padding-top:86.00%;"><img id="XofzJVhShmZ3kcVxuocQvB" name="Ninja Belgian Waffle Maker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/XofzJVhShmZ3kcVxuocQvB.png" mos="" align="middle" fullscreen="" width="1614" height="1388" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow" data-dimension112="9ae6dae8-96fe-11f1-af0b-bffac6dd08f7" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79">Belgian Waffle Maker: was $99 now $79</a></strong><br>Weekend brunch just got an upgrade. This vertical waffle maker has five shade settings so everyone gets their waffle exactly how they like it, plus a specialty setting for high-sugar batters like brownies (yes, really). The precision-pour cup takes the guesswork out of measuring, and audible alerts mean no more burnt disasters. Pair it with the Ninja CREAMi for the ultimate breakfast-dessert combo. <a class="view-deal button" href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow" data-dimension112="9ae6dae8-96fe-11f1-af0b-bffac6dd08f7" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6db42-96fe-11f1-bc49-877570370827" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0C25Y15TL" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:99.56%;"><img id="4uiaRXRKY8Go6F4ZpM7UdA" name="Ninja coffee" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4uiaRXRKY8Go6F4ZpM7UdA.jpg" mos="" align="middle" fullscreen="" width="679" height="676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0C25Y15TL" target="_blank" rel="nofollow" data-dimension112="9ae6db42-96fe-11f1-bc49-877570370827" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">Pods & Grounds Specialty Single-Serve Iced Coffee Maker: was $129 now $99</a></strong><br>This stylish Ninja coffee maker comes in a gorgeous Sage colorway and is now on sale for $30 off. Whether you prefer pods or grounds, this device makes tasty brews in 7 sizes (6-oz. to 24-oz) and has a built-in, fold-away milk frother!<a class="view-deal button" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0C25Y15TL" target="_blank" rel="nofollow" data-dimension112="9ae6db42-96fe-11f1-bc49-877570370827" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dba6-96fe-11f1-abb4-4baee8b836fa" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129" href="https://www.amazon.com/dp/B0DPXNR6YS" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:522px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="pKMqdL3azKMYSNwdTHk4m9" name="Ninja Never Clog" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/pKMqdL3azKMYSNwdTHk4m9.jpg" mos="" align="middle" fullscreen="" width="522" height="522" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0DPXNR6YS" target="_blank" rel="nofollow" data-dimension112="9ae6dba6-96fe-11f1-abb4-4baee8b836fa" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129">NeverClog Cold Press Juicer: was $149 now $129</a></strong><br>Create healthy juices from the comforts of your home with the Ninja Cold Press Juicer Pro. It features a powerful high torque motor and includes two one-touch programs that make it easy to operate. We also like that it has a pulp control that allows you to customize your juice with pulp or without.<a class="view-deal button" href="https://www.amazon.com/dp/B0DPXNR6YS" target="_blank" rel="nofollow" data-dimension112="9ae6dba6-96fe-11f1-abb4-4baee8b836fa" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dc00-96fe-11f1-a154-9dead241a500" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1676px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="sWibiMn9jcqpLxyQRazy6f" name="Ninja Amazon 2.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sWibiMn9jcqpLxyQRazy6f.jpg" mos="" align="middle" fullscreen="" width="1676" height="1676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="9ae6dc00-96fe-11f1-a154-9dead241a500" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179">Mega Kitchen System: was $219 now $179</a></strong><br>Leave all the multi-tasking in the kitchen to one appliance with this Ninja blender. With four functions for smoothies, processing, dough, drinks and more it will quickly become your go-to kitchen appliance. With this ultimate appliance, you’ll be enjoying fresh, healthy drinks, homemade bread and sweet bakes.<a class="view-deal button" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="9ae6dc00-96fe-11f1-a154-9dead241a500" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dc5a-96fe-11f1-9b25-51898ea4d818" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:85.86%;"><img id="zNkXNCVYCubXhCRwjaoGPj" name="Ninjs Crispi" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zNkXNCVYCubXhCRwjaoGPj.jpg" mos="" align="middle" fullscreen="" width="679" height="583" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="9ae6dc5a-96fe-11f1-9b25-51898ea4d818" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">Crispi 4-in-1 Portable Glass Air Fryer: was $179 now $159</a></strong><br>The Ninja Crispi is one of Ninja's most interesting recent releases. It features a removable cooking lid and interchangeable glass containers to cook meals of a range of sizes. In our <a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">Ninja Crispi review</a>, it earned a well-deserved 4.5 stars.  <a class="view-deal button" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="9ae6dc5a-96fe-11f1-9b25-51898ea4d818" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dcbe-96fe-11f1-8347-05248dfd47f6" data-action="Deal Block" data-label="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension48="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension25="$179" href="https://www.amazon.com/Ninja-FrostVaultTM-Backpack-Cooler-Storage/dp/B0GSHG5THD" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:540px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="bcxdGXf8PnyrQEezzPCfXb" name="Ninja FrostVault.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/bcxdGXf8PnyrQEezzPCfXb.jpg" mos="" align="middle" fullscreen="" width="540" height="540" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-FrostVaultTM-Backpack-Cooler-Storage/dp/B0GSHG5THD" target="_blank" rel="nofollow" data-dimension112="9ae6dcbe-96fe-11f1-8347-05248dfd47f6" data-action="Deal Block" data-label="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension48="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension25="$179">FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone: was $199 now $179</a></strong><br>The FrostVault Go is a super convenient backpack cooler from Ninja. It's available in two sizes — this deal is for the 24-can version. It has a Chillseal Lid for maximum ice retention and a Cold Dry Zone to keep food separate and at a food-safe temperature, to ensure its freshness.<a class="view-deal button" href="https://www.amazon.com/Ninja-FrostVaultTM-Backpack-Cooler-Storage/dp/B0GSHG5THD" target="_blank" rel="nofollow" data-dimension112="9ae6dcbe-96fe-11f1-8347-05248dfd47f6" data-action="Deal Block" data-label="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension48="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension25="$179">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dd18-96fe-11f1-97f5-8b56233f0907" data-action="Deal Block" data-label="10-Pc German Steel Knife Set" data-dimension48="10-Pc German Steel Knife Set" data-dimension25="$219" href="https://www.amazon.com/dp/B0H2NLCZBQ" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1368px;"><p class="vanilla-image-block" style="padding-top:109.65%;"><img id="juwy9ZiNjzvb7g4oTRTTT4" name="10-Pc German Steel Knife Set" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/juwy9ZiNjzvb7g4oTRTTT4.jpg" mos="" align="middle" fullscreen="" width="1368" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0H2NLCZBQ" target="_blank" rel="nofollow" data-dimension112="9ae6dd18-96fe-11f1-97f5-8b56233f0907" data-action="Deal Block" data-label="10-Pc German Steel Knife Set" data-dimension48="10-Pc German Steel Knife Set" data-dimension25="$219">10-Pc German Steel Knife Set: was $249 now $219</a></strong><br>This 10-piece knife set from Ninja provides all the knives you need to cut, chop and dice your food. It also features a built-in knife sharpener, so you never have an excuse to use a blunt knife again — it'll keep them sharp for 10+ years.<a class="view-deal button" href="https://www.amazon.com/dp/B0H2NLCZBQ" target="_blank" rel="nofollow" data-dimension112="9ae6dd18-96fe-11f1-97f5-8b56233f0907" data-action="Deal Block" data-label="10-Pc German Steel Knife Set" data-dimension48="10-Pc German Steel Knife Set" data-dimension25="$219">View Deal</a></p></div><div class="product"><a data-dimension112="c39b8028-96ff-11f1-98e3-63e3970b16f9" data-action="Deal Block" data-label="Double Oven, 12-in-1 DCT401" data-dimension48="Double Oven, 12-in-1 DCT401" data-dimension25="$219" href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:875px;"><p class="vanilla-image-block" style="padding-top:85.60%;"><img id="LQBDeKyzWKbLWRQgh54YmA" name="Double Oven, 12-in-1 DCT401" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/LQBDeKyzWKbLWRQgh54YmA.jpg" mos="" align="middle" fullscreen="" width="875" height="749" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow" data-dimension112="c39b8028-96ff-11f1-98e3-63e3970b16f9" data-action="Deal Block" data-label="Double Oven, 12-in-1 DCT401" data-dimension48="Double Oven, 12-in-1 DCT401" data-dimension25="$219">Double Oven, 12-in-1 DCT401: was $379 now $219</a></strong><br>We couldn’t recommend this toaster oven more. Although it has since been replaced by a newer model, the DCT401 is a previous winner on our list of the <a href="https://www.tomsguide.com/best-picks/best-toaster-ovens">best toaster ovens</a>. We love that it divides into two ovens you can access separately via the FlexDoor, so you can cook two things at once. There are 12 functions overall, including toast, bake, whole roast, air fry, convection and pizza — meaning you can cook pretty much anything in there however you like.<a class="view-deal button" href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow" data-dimension112="c39b8028-96ff-11f1-98e3-63e3970b16f9" data-action="Deal Block" data-label="Double Oven, 12-in-1 DCT401" data-dimension48="Double Oven, 12-in-1 DCT401" data-dimension25="$219">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dd72-96fe-11f1-88b3-e97212624547" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229" href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:75.80%;"><img id="sFc44K8P9eTgsrZ4Kc25oB" name="Foodi XL Pro Air Fry Oven, 10-in-1" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sFc44K8P9eTgsrZ4Kc25oB.jpg" mos="" align="middle" fullscreen="" width="1500" height="1137" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow" data-dimension112="9ae6dd72-96fe-11f1-88b3-e97212624547" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229">Foodi XL Pro Air Fry Oven, 10-in-1: was $379 now $229</a></strong><br>39% off is one of the biggest Ninja discounts you'll find right now. While this XL air fryer/oven takes up a lot of space, it offers plenty of cooking functions and perfect cooking results every time. <a class="view-deal button" href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow" data-dimension112="9ae6dd72-96fe-11f1-88b3-e97212624547" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6ddd6-96fe-11f1-a3e0-737367858cbe" data-action="Deal Block" data-label="Woodfire Oven & Smoker" data-dimension48="Woodfire Oven & Smoker" data-dimension25="$279" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:90.20%;"><img id="C5KWDYUzjspNSP6tT8AAQP" name="81IeXOFExqL._AC_SL1500_.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/C5KWDYUzjspNSP6tT8AAQP.jpg" mos="" align="middle" fullscreen="" width="1500" height="1353" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Woodfire Oven & Smoker is a favorite with the Tom's Guide team. It makes amazing pizzas, smoked wings and many things in between. It's perfect to keep the summer vibes going all year long with delicious dishes.<a class="view-deal button" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow" data-dimension112="9ae6ddd6-96fe-11f1-a3e0-737367858cbe" data-action="Deal Block" data-label="Woodfire Oven & Smoker" data-dimension48="Woodfire Oven & Smoker" data-dimension25="$279">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6de30-96fe-11f1-93b8-cf7bb06625b2" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599" href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:541px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="HcnQ36qmUJg5VJidxsJ76X" name="Ninja Luxe Cafe.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/HcnQ36qmUJg5VJidxsJ76X.jpg" mos="" align="middle" fullscreen="" width="541" height="541" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>It's not on sale, but the Ninja Luxe Café Premier is just that good. This 3-in-1 coffee machine brews quality espresso, drip coffee, and fast cold brew using its Barista Assist technology, an integrated grinder, and weight-based dosing. Plus, it makes creating pro-level microfoam easy, thanks to its Dual Froth system. Our reviewer called it "the best beginner-friendly espresso machine I’ve ever tested" in our <a href="https://www.tomsguide.com/home/ninja-luxe-cafe-premier-series-espresso-machine-review" data-dimension112="9ae6de30-96fe-11f1-93b8-cf7bb06625b2" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599">Ninja Luxe Café coffee maker review</a>.<a class="view-deal button" href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow" data-dimension112="9ae6de30-96fe-11f1-93b8-cf7bb06625b2" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599">View Deal</a></p></div><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Home+Appliances&brands=Ninja%2CShark&min_discount_ratio=0.95&offer_type=all&view_mode=carousel&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=light_blue" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/LCxBaXIgdGFncywsLCwsLCwsLCwsLCwsDQosLFBvcnRhYmxlIGNoYXJnZXJzL3dpcmVsZXNzIGNoYXJnZXJzLCwsLCwsLCwsLCwsLCwNCiwsIldhdGVyIGJvdHRsZXMgKHN0YW5sZXlzLCBPd2FsYSwgSHlkcm8gZmxhc2ssIFlldGkpIiwsLCwsLCwsLCwsLCwsDQosLEhhbmQgaGVsZCBmYW5zLCwsLCwsLCwsLCwsLCwNCiwsLCwsLCwsLCwsLCwsLCwNCmhvbWUgb2ZmaWNlLCxvZmZpY2UgY2hhaXJzLCwsLCwsLCwsLCwsLCwNCiwsc3RhbmRpbmcgZGVza3MsLCwsLCwsLCwsLCwsLA0KLCxtb25pdG9ycywsLCwsLCwsLCwsLCwsDQosLEtleWJvYXJkcywsLCwsLCwsLCwsLCwsDQosLGRvY2tpbmcgc3RhdGlvbiwsLCwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpHYW1pbmcsLENvbnNvbGVzLCwsLCwsLCwsLCwsLCwNCiwsQWNjZXNzb3JpZXMsLCwsLCwsLCwsLCwsLA0KLCxHYW1lcywsLCwsLCwsLCwsLCwsDQosLENvdWxkIGluY2x1ZGUgTGVnbz8sLCwsLCwsLCwsLCwsLA==")));              const text = preloadedCSV;              const parsed = parseCSV(text);                            const rowsByName = {};              let filterStart = -1;              parsed.forEach((rc, i) => {                 if (rc && rc.length > 0 && rc[0]) rowsByName[rc[0]] = rc;                 if (rc && rc.length > 0 && rc[0] === 'Filter buttons') filterStart = i;              });                            const cols = [];              if(rowsByName['Roundel text']) {                const headerRow = rowsByName['Roundel text'];                for(let col = 1; col < headerRow.length; col++) {                   let label = headerRow[col];                   if (!label) continue;                                      let q = rowsByName['Search Query'] && rowsByName['Search Query'][col] ? rowsByName['Search Query'][col] : '';                   let img = rowsByName['Roundel image'] && rowsByName['Roundel image'][col] ? rowsByName['Roundel image'][col] : '';                   let ds = rowsByName['Discount Amount'] && rowsByName['Discount Amount'][col] ? rowsByName['Discount Amount'][col] : '';                   let pr = rowsByName['Price Range'] && rowsByName['Price Range'][col] ? rowsByName['Price Range'][col] : '';                   let rt = rowsByName['Retailer'] && rowsByName['Retailer'][col] ? rowsByName['Retailer'][col] : '';                   let ot = rowsByName['Offer Type'] && rowsByName['Offer Type'][col] ? rowsByName['Offer Type'][col] : '';                                      let filters = [];                   if(filterStart > 0) {                     for(let r = filterStart + 1; r < parsed.length; r++) {                         if(!parsed[r] || parsed[r][0] === 'Notes' || parsed[r][0] === 'Categories to consider') break;                         let f = parsed[r][col];                         if(f) filters.push(f);                     }                   }                   cols.push({ label, img, q, ds, pr, rt, ot, filters });                }              }              this.carouselData = cols;              if (this.carouselData && this.carouselData.length > 0) {                 const isMatched = this.carouselData.some(c => c.q === this.currentQuery || c.label === this.currentQuery);                 if (!isMatched) {                    const first = this.carouselData[0];                    this.currentQuery = first.q || first.label;                    if (this.priceFilter) this.priceFilter.value = 'all';                    if (this.customPriceMin) this.customPriceMin.value = '';                    if (this.customPriceMax) this.customPriceMax.value = '';                    let dPr = first.pr || 'all';                    if (typeof dPr === 'string' && dPr !== 'all') {                       let prLower = dPr.toLowerCase();                       if (prLower.includes('min') || prLower.includes('over')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else if (prLower.includes('max') || prLower.includes('under')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       }                    }                    let dAm = '0';                    if(first.ds && typeof first.ds === 'string') {                       let m = first.ds.match(/(\d+)/);                       if(m) dAm = m[1];                    }                    if (this.discountFilter) this.discountFilter.value = dAm;                    if (this.offerTypeSelect) this.offerTypeSelect.value = first.ot || '';                    if (this.retailerSelect) this.retailerSelect.value = first.rt || '';                    this.selectedBrands = [];                    if (this.brandDropdown) {                        const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                        chks.forEach(chk => chk.checked = false);                    }                    if (this.searchInput) this.searchInput.value = this.currentQuery;                 }              }              this.renderCarouselUI();          } catch(e){ console.warn(e); }        }                renderCarouselUI() {           const roundelWrapper = this.root.querySelector('.tg-df-carousel-roundels');           if(!roundelWrapper || !this.carouselData) return;                      let html = '';           this.carouselData.forEach(r => {              const q = r.q || r.label;              const isActive = (this.currentQuery === q || this.currentQuery === r.label) ? 'active' : '';              const imgHtml = r.img ? `\x3Cimg src="${r.img}" alt="${r.label}" />` : `\x3Csvg width="32" height="32" fill="#1F69FF" viewBox="0 0 24 24">\x3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\x3C/svg>`;              html += `                \x3Cdiv class="tg-df-roundel tg-df-carousel-cat ${isActive}" data-label="${this.escapeHTML(r.label)}">                  \x3Cdiv class="tg-df-roundel-img-box">${imgHtml}\x3C/div>                  \x3Cspan class="tg-df-roundel-label">${this.escapeHTML(r.label)}\x3C/span>                \x3C/div>              `;           });           roundelWrapper.innerHTML = html;                      // Rebind clicks           const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');           roundels.forEach(rNode => {             rNode.addEventListener('click', () => {                const r = this.carouselData.find(c => c.label === rNode.getAttribute('data-label'));                 if(!r) return;                                  if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: r.label,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = r.q || r.label;                const labelTitle = this.root.querySelector('#tg-df-carousel-title-label');                if (labelTitle) labelTitle.textContent = 'Best ' + this.currentQuery;                if (this.priceFilter) this.priceFilter.value = 'all';                if (this.customPriceMin) this.customPriceMin.value = '';                if (this.customPriceMax) this.customPriceMax.value = '';                let dPr = r.pr || 'all';                if (typeof dPr === 'string' && dPr !== 'all') {                   let prLower = dPr.toLowerCase();                   if (prLower.includes('min') || prLower.includes('over')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMin) this.customPriceMin.value = m[1];                   } else if (prLower.includes('max') || prLower.includes('under')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMax) this.customPriceMax.value = m[1];                   }                }                                let discountAmount = '0';                if(r.ds && typeof r.ds === 'string') {                   let m = r.ds.match(/(\d+)/);                   if(m) discountAmount = m[1];                }                if (this.discountFilter) this.discountFilter.value = discountAmount;                if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                                // Clear brands                    this.selectedBrands = [];                    if (this.brandDropdown) {                    const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                    chks.forEach(chk => chk.checked = false);                }                                if (this.searchInput) this.searchInput.value = this.currentQuery;                                roundels.forEach(ro => ro.classList.remove('active'));                if (rNode) rNode.classList.add('active');                                this.renderCarouselFilters(r);                this.fetchDeals(this.currentQuery);             });           });                      // Auto-highlight active           const activeR = this.carouselData.find(c => c.q === this.currentQuery || c.label === this.currentQuery);           if(activeR) this.renderCarouselFilters(activeR);        }                renderCarouselFilters(r) {           const filtersWrap = this.root.querySelector('.tg-df-carousel-filters-wrap');           if(!filtersWrap) return;                      let html = `\x3Cbutton class="tg-df-carousel-filter-btn" data-type="all">All\x3C/button>`;                      r.filters.forEach(f => {              let fL = f.toLowerCase();              let icon = '';              let logic = `data-type="custom" data-v="${this.escapeHTML(f)}"`;              if (fL === 'amazon deals' || fL === 'prime deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>`;              } else if (fL === 'lightning deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>`;              } else {                 if (fL.includes('lightning')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap">\x3Cpolygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2">\x3C/polygon>\x3C/svg>`;                 } else if (fL.includes('% off')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>`;                 } else if (fL.includes('under') || fL.includes('min ')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>`;                 }                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>${icon} ${this.escapeHTML(f)}\x3C/button>`;              }           });                      filtersWrap.innerHTML = html;                      const btns = filtersWrap.querySelectorAll('button');           btns.forEach(b => {             b.addEventListener('click', () => {                const type = b.getAttribute('data-type');                if (type === 'custom') {                   const v = b.getAttribute('data-v');                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-custom-${(v||'').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`, name: 'Custom Filter', label: v });                }                if (type === 'all') {                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: 'filter-clear-all', name: 'Clear all', label: 'Clear all filters' });                   // reset everything                   btns.forEach(btn => btn.classList.remove('active'));                   b.classList.add('active');                                      // Reset prices                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   if (this.brandDropdown) {                     const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                     chks.forEach(chk => chk.checked = false);                   }                } else {                   const v = b.getAttribute('data-v');                   const fL = v.toLowerCase();                                      let mapRet = ['amazon', 'walmart', 'best buy', 'target', 'john lewis', 'currys', 'argos'];                   const getCategory = (s) => {                      if (s === 'lightning deals' || s === 'amazon deals' || s === 'prime deals') return 'offer';                      if (s.includes('% off')) return 'discount';                      if (s.includes('under') || s.includes('over') || s.includes('min') || s.includes('max')) return 'price';                      if (mapRet.includes(s)) return 'retailer';                      return 'brand';                   };                   const cat = getCategory(fL);                   const wasActive = b.classList.contains('active');                   if (cat !== 'brand') {                      btns.forEach(btn => {                          if (btn === b) return;                          if (btn.getAttribute('data-type') === 'all') return;                          const bV = btn.getAttribute('data-v');                          if (!bV) return;                          if (getCategory(bV.toLowerCase()) === cat) btn.classList.remove('active');                      });                   }                   if (wasActive) b.classList.remove('active');                   else b.classList.add('active');                   let anyActive = Array.from(btns).some(btn => btn !== btns[0] && btn.classList.contains('active'));                   if (!anyActive) {                       btns[0].click();                       return;                   } else {                       btns[0].classList.remove('active');                   }                                      if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   btns.forEach(btn => {                       if (!btn.classList.contains('active') || btn.getAttribute('data-type') === 'all') return;                       const vv = btn.getAttribute('data-v');                       const vl = vv.toLowerCase();                                              if (vl === 'lightning deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_lightning';                       } else if (vl === 'amazon deals' || vl === 'prime deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_prime';                       } else if (vl.includes('% off')) {                          let m = vl.match(/(\d+)%/);                          if (m && this.discountFilter) this.discountFilter.value = m[1];                       } else if (vl.includes('under') || vl.includes('max')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       } else if (vl.includes('min') || vl.includes('over')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else {                          let foundR = mapRet.find(x => x === vl);                          if (foundR) {                             let realR = ['Amazon', 'Walmart', 'Best Buy', 'Target', 'John Lewis', 'Currys', 'Argos'].find(x => x.toLowerCase() === vl);                             if (this.retailerSelect) this.retailerSelect.value = realR;                          } else {                             this.selectedBrands.push(vv);                          }                       }                   });                                      if (this.brandDropdown) {                       const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                       chks.forEach(c => c.checked = this.selectedBrands.includes(c.value));                   }                                      if (r.pr && typeof r.pr === 'string') {                       let prL = r.pr.toLowerCase();                       if (prL.includes('under $')) {                           let m = prL.match(/under \$(\d+)/i);                           if (m && this.customPriceMax && !this.customPriceMax.value) this.customPriceMax.value = m[1];                       }                   }                }                                this.fetchDeals(this.currentQuery);             });           });                      // default to highlighting first           btns[0].classList.add('active');        }async fetchDeals(query, append = false) {          if (!append) {             this.showLoading();             this.deals = [];             this.displayLimit = this.getRowsLimit();          } else {             this.displayLimit += this.getRowsLimit();          }                    try {            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad(append);            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query, append);               } else {                 await this.fetchHawkDeals(query, append);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query, append);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const url = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let dynamicBrandsCounts = {};                    targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                   dynamicBrandsCounts[data.brand] = (dynamicBrandsCounts[data.brand] || 0) + 1;                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || ''                });             });          });                    const airedaleBrandsList = Object.keys(dynamicBrandsCounts).map(b => ({              formatted_value: b,              count: dynamicBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query, append = false) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (append && this.deals.length > 0) {            url.searchParams.append('offset', this.deals.length.toString());          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : (this.getViewMode() === 'savings_squad' ? 'date_desc' : 'discount_desc');          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin-top:8px;">${_div}                ${_div}              ${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)} (${this.airedaleTagCounts[tag] || 0})${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = isSavingsSquadMode ? 'View Deal' : 'Check Price';              if (isSavingsSquadMode) {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                  ${_div}                `;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = `                \x3Cdiv class="tg-df-card-merchant-wrapper">                  \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                ${_div}                \x3Cdiv class="tg-df-card-price-group">                  ${isSavingsSquadMode ? '' : `                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                  `}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description">\x3Cp>${this.escapeHTML(deal.description)}${_p}${_div}` : ''}                      ${_div}                    ${_div}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>${deal.isCheckPrice ? 'Check Price' : 'View Deal'}${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cp style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 12px; line-height: 1.4;">${this.escapeHTML(deal.description)}${_p}` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${priceGroupHtml}                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta ${isSavingsSquadMode ? 'tg-df-cta-savings-squad' : ''}" style="text-decoration: none;">${ctaText}${_a}                  ${_div}                ${_div}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit || ((this.getViewMode() === 'carousel' || this.getViewMode() === 'auto') && displayDeals.length > 0 && displayDeals.length % ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12) === 0)) {            if (this.getViewMode() === 'carousel') {               dealsHtml += `                 \x3Cbutton type="button" class="tg-df-load-more-card tg-df-load-more">                   \x3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;">\x3Cpath d="M5 12h14">\x3C/path>\x3Cpath d="m12 5 7 7-7 7">\x3C/path>\x3C/svg>                   Load More                 ${_button}               `;            } else {               dealsHtml += `                 \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                   \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer; display: flex; align-items: center;">Load More${_button}                 ${_div}               `;            }          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let rightChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             let leftChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-left');             if (this.getViewMode() === 'carousel') {                 // The observer set up in setupScrollListeners handles visibility.                 if (rightChevron) rightChevron.style.display = 'flex';                 if (leftChevron) leftChevron.style.display = 'none'; // reset correctly             } else {                 if (rightChevron) rightChevron.style.display = 'none';                 if (leftChevron) leftChevron.style.display = 'none';             }          }                    const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', async () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              if (displayDeals.length <= this.displayLimit) {                 loadMoreBtn.innerHTML = `                  <svg class="tg-df-spinner" style="width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 8px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/></svg>                  Loading...                 `;                 loadMoreBtn.disabled = true;                 await this.fetchDeals(this.currentQuery, true);              } else {                 this.displayLimit += this.getRowsLimit();                 this.render();              }            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            setTimeout(() => {               if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');               document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));            }, 50);          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/dont-let-summer-end-ninja-pizza-ovens-blenders-and-coolers-are-up-to-42-percent-off-at-amazon</link>
                                                                            <description>
                            <![CDATA[ A bunch of Ninja appliances are on sale at Amazon — check out the biggest upgrades for your home and kitchen. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">EfTBZoT6Fwj3CeUdpAZ3aV</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/CeAv82LBUHuqBFDCWGzZqb-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 13 Aug 2026 16:23:09 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ millie.daviswilliams@futurenet.com (Millie Davis-Williams) ]]></author>                    <dc:creator><![CDATA[ Millie Davis-Williams ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/WU6pa7hKuxs5ScCoqcXYWA.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Millie is a Deals Editor at Tom&#039;s Guide specializing in tech deals content. Based in London, Millie has spent a lifetime stubbornly refusing to pay full price on any possible purchase. She has years of searching for the best discounts on the latest and greatest phones, TVs and gadgets under her belt, and knows how to sort the best deals from the rest. Helping readers save on the products they want is her mission, whether it’s an Apple Watch or a new tablet.&lt;/p&gt;&lt;p&gt;Since working at Tom’s Guide, Millie has covered retail events like Prime Day, Black Friday and Cyber Monday extensively, meaning she knows the products readers are looking for and the best times of year to hit “purchase.”&lt;/p&gt;&lt;p&gt;When she’s not looking for discounts, Millie covers news and writes how-to articles about everything from phones, streaming devices, and headphones to apps and video games.&lt;/p&gt;&lt;p&gt;In her spare time, Millie enjoys reading, gaming on her Nintendo Switch and creating digital art. She’s also a fan of watching Disney movies and collecting plush (the latter having totally taken over her bedroom.)&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/CeAv82LBUHuqBFDCWGzZqb-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Ninja Blast Max blender on counter]]></media:description>                                                            <media:text><![CDATA[Ninja Blast Max blender on counter]]></media:text>
                                <media:title type="plain"><![CDATA[Ninja Blast Max blender on counter]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/CeAv82LBUHuqBFDCWGzZqb-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Amazon is closing out the summer with a bunch of Ninja appliance deals. Whether you're hoping to extend grilling season into the fall, want to keep enjoying delicious smoothies, or even improve your knife skills while cooking — there are some discounts here that you definitely don't want to miss.</p><p>I've been searching the sale and have found <a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow">up to 30% off Ninja appliances</a>. I've listed all my favorites below, so keep scrolling to check them out.</p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow"><strong>shop all Ninja deals at Amazon</strong></a></li><li><strong>Ninja Blast Max: </strong><a href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow"><strong>was $99 now $69</strong></a><strong></strong></li><li><strong>Ninja Iced Coffee Maker: </strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0C25Y15TL" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Ninja Crispi 4-in-1 Portable Glass Air Fryer: </strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><strong>was $179 now $159</strong></a></li><li><strong>Ninja 10-Pc German Steel Knife Set: </strong><a href="https://www.amazon.com/dp/B0H2NLCZBQ" target="_blank" rel="nofollow"><strong>was $249 now $199</strong></a></li><li><strong>Ninja Woodfire Oven & Smoker: </strong><a href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow"><strong>was $399 now $279</strong></a></li><li><strong>Ninja Luxe Café Premier: </strong><a href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow"><strong>now $599</strong></a></li></ul><h3 class="article-body__section" id="section-ninja-deals"><span>Ninja Deals</span></h3><div class="product"><a data-dimension112="9ae6da84-96fe-11f1-a330-f9d854e50872" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69" href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1216px;"><p class="vanilla-image-block" style="padding-top:123.36%;"><img id="9Q4QY399FaozAFUt4j8dTW" name="ninja-blast-max-portable-blender--twist--bb517479-54ce-4a77-974c-58e2f44dde33.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/9Q4QY399FaozAFUt4j8dTW.jpg" mos="" align="middle" fullscreen="" width="1216" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Blast Max is capable of taking on any challenge you throw at it, including crushing ice and blitzing berries to make yummy smoothies. It comes with multiple blend settings to make sure your blender is using the right level of power for the task at hand, and the compact size means it’s a great option for anyone with a small kitchen. <a class="view-deal button" href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow" data-dimension112="9ae6da84-96fe-11f1-a330-f9d854e50872" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dae8-96fe-11f1-af0b-bffac6dd08f7" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79" href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1614px;"><p class="vanilla-image-block" style="padding-top:86.00%;"><img id="XofzJVhShmZ3kcVxuocQvB" name="Ninja Belgian Waffle Maker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/XofzJVhShmZ3kcVxuocQvB.png" mos="" align="middle" fullscreen="" width="1614" height="1388" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow" data-dimension112="9ae6dae8-96fe-11f1-af0b-bffac6dd08f7" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79">Belgian Waffle Maker: was $99 now $79</a></strong><br>Weekend brunch just got an upgrade. This vertical waffle maker has five shade settings so everyone gets their waffle exactly how they like it, plus a specialty setting for high-sugar batters like brownies (yes, really). The precision-pour cup takes the guesswork out of measuring, and audible alerts mean no more burnt disasters. Pair it with the Ninja CREAMi for the ultimate breakfast-dessert combo. <a class="view-deal button" href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow" data-dimension112="9ae6dae8-96fe-11f1-af0b-bffac6dd08f7" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6db42-96fe-11f1-bc49-877570370827" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0C25Y15TL" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:99.56%;"><img id="4uiaRXRKY8Go6F4ZpM7UdA" name="Ninja coffee" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4uiaRXRKY8Go6F4ZpM7UdA.jpg" mos="" align="middle" fullscreen="" width="679" height="676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0C25Y15TL" target="_blank" rel="nofollow" data-dimension112="9ae6db42-96fe-11f1-bc49-877570370827" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">Pods & Grounds Specialty Single-Serve Iced Coffee Maker: was $129 now $99</a></strong><br>This stylish Ninja coffee maker comes in a gorgeous Sage colorway and is now on sale for $30 off. Whether you prefer pods or grounds, this device makes tasty brews in 7 sizes (6-oz. to 24-oz) and has a built-in, fold-away milk frother!<a class="view-deal button" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0C25Y15TL" target="_blank" rel="nofollow" data-dimension112="9ae6db42-96fe-11f1-bc49-877570370827" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dba6-96fe-11f1-abb4-4baee8b836fa" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129" href="https://www.amazon.com/dp/B0DPXNR6YS" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:522px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="pKMqdL3azKMYSNwdTHk4m9" name="Ninja Never Clog" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/pKMqdL3azKMYSNwdTHk4m9.jpg" mos="" align="middle" fullscreen="" width="522" height="522" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0DPXNR6YS" target="_blank" rel="nofollow" data-dimension112="9ae6dba6-96fe-11f1-abb4-4baee8b836fa" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129">NeverClog Cold Press Juicer: was $149 now $129</a></strong><br>Create healthy juices from the comforts of your home with the Ninja Cold Press Juicer Pro. It features a powerful high torque motor and includes two one-touch programs that make it easy to operate. We also like that it has a pulp control that allows you to customize your juice with pulp or without.<a class="view-deal button" href="https://www.amazon.com/dp/B0DPXNR6YS" target="_blank" rel="nofollow" data-dimension112="9ae6dba6-96fe-11f1-abb4-4baee8b836fa" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dc00-96fe-11f1-a154-9dead241a500" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1676px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="sWibiMn9jcqpLxyQRazy6f" name="Ninja Amazon 2.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sWibiMn9jcqpLxyQRazy6f.jpg" mos="" align="middle" fullscreen="" width="1676" height="1676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="9ae6dc00-96fe-11f1-a154-9dead241a500" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179">Mega Kitchen System: was $219 now $179</a></strong><br>Leave all the multi-tasking in the kitchen to one appliance with this Ninja blender. With four functions for smoothies, processing, dough, drinks and more it will quickly become your go-to kitchen appliance. With this ultimate appliance, you’ll be enjoying fresh, healthy drinks, homemade bread and sweet bakes.<a class="view-deal button" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="9ae6dc00-96fe-11f1-a154-9dead241a500" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dc5a-96fe-11f1-9b25-51898ea4d818" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:85.86%;"><img id="zNkXNCVYCubXhCRwjaoGPj" name="Ninjs Crispi" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zNkXNCVYCubXhCRwjaoGPj.jpg" mos="" align="middle" fullscreen="" width="679" height="583" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="9ae6dc5a-96fe-11f1-9b25-51898ea4d818" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">Crispi 4-in-1 Portable Glass Air Fryer: was $179 now $159</a></strong><br>The Ninja Crispi is one of Ninja's most interesting recent releases. It features a removable cooking lid and interchangeable glass containers to cook meals of a range of sizes. In our <a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">Ninja Crispi review</a>, it earned a well-deserved 4.5 stars.  <a class="view-deal button" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="9ae6dc5a-96fe-11f1-9b25-51898ea4d818" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dcbe-96fe-11f1-8347-05248dfd47f6" data-action="Deal Block" data-label="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension48="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension25="$179" href="https://www.amazon.com/Ninja-FrostVaultTM-Backpack-Cooler-Storage/dp/B0GSHG5THD" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:540px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="bcxdGXf8PnyrQEezzPCfXb" name="Ninja FrostVault.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/bcxdGXf8PnyrQEezzPCfXb.jpg" mos="" align="middle" fullscreen="" width="540" height="540" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-FrostVaultTM-Backpack-Cooler-Storage/dp/B0GSHG5THD" target="_blank" rel="nofollow" data-dimension112="9ae6dcbe-96fe-11f1-8347-05248dfd47f6" data-action="Deal Block" data-label="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension48="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension25="$179">FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone: was $199 now $179</a></strong><br>The FrostVault Go is a super convenient backpack cooler from Ninja. It's available in two sizes — this deal is for the 24-can version. It has a Chillseal Lid for maximum ice retention and a Cold Dry Zone to keep food separate and at a food-safe temperature, to ensure its freshness.<a class="view-deal button" href="https://www.amazon.com/Ninja-FrostVaultTM-Backpack-Cooler-Storage/dp/B0GSHG5THD" target="_blank" rel="nofollow" data-dimension112="9ae6dcbe-96fe-11f1-8347-05248dfd47f6" data-action="Deal Block" data-label="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension48="FrostVault Go 24-Can Backpack Soft Cooler with Cold Dry Zone" data-dimension25="$179">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dd18-96fe-11f1-97f5-8b56233f0907" data-action="Deal Block" data-label="10-Pc German Steel Knife Set" data-dimension48="10-Pc German Steel Knife Set" data-dimension25="$219" href="https://www.amazon.com/dp/B0H2NLCZBQ" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1368px;"><p class="vanilla-image-block" style="padding-top:109.65%;"><img id="juwy9ZiNjzvb7g4oTRTTT4" name="10-Pc German Steel Knife Set" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/juwy9ZiNjzvb7g4oTRTTT4.jpg" mos="" align="middle" fullscreen="" width="1368" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0H2NLCZBQ" target="_blank" rel="nofollow" data-dimension112="9ae6dd18-96fe-11f1-97f5-8b56233f0907" data-action="Deal Block" data-label="10-Pc German Steel Knife Set" data-dimension48="10-Pc German Steel Knife Set" data-dimension25="$219">10-Pc German Steel Knife Set: was $249 now $219</a></strong><br>This 10-piece knife set from Ninja provides all the knives you need to cut, chop and dice your food. It also features a built-in knife sharpener, so you never have an excuse to use a blunt knife again — it'll keep them sharp for 10+ years.<a class="view-deal button" href="https://www.amazon.com/dp/B0H2NLCZBQ" target="_blank" rel="nofollow" data-dimension112="9ae6dd18-96fe-11f1-97f5-8b56233f0907" data-action="Deal Block" data-label="10-Pc German Steel Knife Set" data-dimension48="10-Pc German Steel Knife Set" data-dimension25="$219">View Deal</a></p></div><div class="product"><a data-dimension112="c39b8028-96ff-11f1-98e3-63e3970b16f9" data-action="Deal Block" data-label="Double Oven, 12-in-1 DCT401" data-dimension48="Double Oven, 12-in-1 DCT401" data-dimension25="$219" href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:875px;"><p class="vanilla-image-block" style="padding-top:85.60%;"><img id="LQBDeKyzWKbLWRQgh54YmA" name="Double Oven, 12-in-1 DCT401" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/LQBDeKyzWKbLWRQgh54YmA.jpg" mos="" align="middle" fullscreen="" width="875" height="749" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow" data-dimension112="c39b8028-96ff-11f1-98e3-63e3970b16f9" data-action="Deal Block" data-label="Double Oven, 12-in-1 DCT401" data-dimension48="Double Oven, 12-in-1 DCT401" data-dimension25="$219">Double Oven, 12-in-1 DCT401: was $379 now $219</a></strong><br>We couldn’t recommend this toaster oven more. Although it has since been replaced by a newer model, the DCT401 is a previous winner on our list of the <a href="https://www.tomsguide.com/best-picks/best-toaster-ovens">best toaster ovens</a>. We love that it divides into two ovens you can access separately via the FlexDoor, so you can cook two things at once. There are 12 functions overall, including toast, bake, whole roast, air fry, convection and pizza — meaning you can cook pretty much anything in there however you like.<a class="view-deal button" href="https://www.amazon.com/Ninja-DCT401-FlavorSeal-Convection-Stainless/dp/B0BRQS5BH6" target="_blank" rel="nofollow" data-dimension112="c39b8028-96ff-11f1-98e3-63e3970b16f9" data-action="Deal Block" data-label="Double Oven, 12-in-1 DCT401" data-dimension48="Double Oven, 12-in-1 DCT401" data-dimension25="$219">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6dd72-96fe-11f1-88b3-e97212624547" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229" href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:75.80%;"><img id="sFc44K8P9eTgsrZ4Kc25oB" name="Foodi XL Pro Air Fry Oven, 10-in-1" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sFc44K8P9eTgsrZ4Kc25oB.jpg" mos="" align="middle" fullscreen="" width="1500" height="1137" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow" data-dimension112="9ae6dd72-96fe-11f1-88b3-e97212624547" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229">Foodi XL Pro Air Fry Oven, 10-in-1: was $379 now $229</a></strong><br>39% off is one of the biggest Ninja discounts you'll find right now. While this XL air fryer/oven takes up a lot of space, it offers plenty of cooking functions and perfect cooking results every time. <a class="view-deal button" href="https://www.amazon.com/Ninja-DT201-Countertop-Convection-Dehydrate/dp/B08BXX69K4" target="_blank" rel="nofollow" data-dimension112="9ae6dd72-96fe-11f1-88b3-e97212624547" data-action="Deal Block" data-label="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension48="Foodi XL Pro Air Fry Oven, 10-in-1" data-dimension25="$229">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6ddd6-96fe-11f1-a3e0-737367858cbe" data-action="Deal Block" data-label="Woodfire Oven & Smoker" data-dimension48="Woodfire Oven & Smoker" data-dimension25="$279" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:90.20%;"><img id="C5KWDYUzjspNSP6tT8AAQP" name="81IeXOFExqL._AC_SL1500_.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/C5KWDYUzjspNSP6tT8AAQP.jpg" mos="" align="middle" fullscreen="" width="1500" height="1353" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Woodfire Oven & Smoker is a favorite with the Tom's Guide team. It makes amazing pizzas, smoked wings and many things in between. It's perfect to keep the summer vibes going all year long with delicious dishes.<a class="view-deal button" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3/" target="_blank" rel="nofollow" data-dimension112="9ae6ddd6-96fe-11f1-a3e0-737367858cbe" data-action="Deal Block" data-label="Woodfire Oven & Smoker" data-dimension48="Woodfire Oven & Smoker" data-dimension25="$279">View Deal</a></p></div><div class="product"><a data-dimension112="9ae6de30-96fe-11f1-93b8-cf7bb06625b2" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599" href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:541px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="HcnQ36qmUJg5VJidxsJ76X" name="Ninja Luxe Cafe.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/HcnQ36qmUJg5VJidxsJ76X.jpg" mos="" align="middle" fullscreen="" width="541" height="541" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>It's not on sale, but the Ninja Luxe Café Premier is just that good. This 3-in-1 coffee machine brews quality espresso, drip coffee, and fast cold brew using its Barista Assist technology, an integrated grinder, and weight-based dosing. Plus, it makes creating pro-level microfoam easy, thanks to its Dual Froth system. Our reviewer called it "the best beginner-friendly espresso machine I’ve ever tested" in our <a href="https://www.tomsguide.com/home/ninja-luxe-cafe-premier-series-espresso-machine-review" data-dimension112="9ae6de30-96fe-11f1-93b8-cf7bb06625b2" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599">Ninja Luxe Café coffee maker review</a>.<a class="view-deal button" href="https://www.amazon.com/dp/B0FGDTHY1Y" target="_blank" rel="nofollow" data-dimension112="9ae6de30-96fe-11f1-93b8-cf7bb06625b2" data-action="Deal Block" data-label="Luxe Café Premier" data-dimension48="Luxe Café Premier" data-dimension25="$599">View Deal</a></p></div><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Home+Appliances&brands=Ninja%2CShark&min_discount_ratio=0.95&offer_type=all&view_mode=carousel&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=light_blue" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/LCxBaXIgdGFncywsLCwsLCwsLCwsLCwsDQosLFBvcnRhYmxlIGNoYXJnZXJzL3dpcmVsZXNzIGNoYXJnZXJzLCwsLCwsLCwsLCwsLCwNCiwsIldhdGVyIGJvdHRsZXMgKHN0YW5sZXlzLCBPd2FsYSwgSHlkcm8gZmxhc2ssIFlldGkpIiwsLCwsLCwsLCwsLCwsDQosLEhhbmQgaGVsZCBmYW5zLCwsLCwsLCwsLCwsLCwNCiwsLCwsLCwsLCwsLCwsLCwNCmhvbWUgb2ZmaWNlLCxvZmZpY2UgY2hhaXJzLCwsLCwsLCwsLCwsLCwNCiwsc3RhbmRpbmcgZGVza3MsLCwsLCwsLCwsLCwsLA0KLCxtb25pdG9ycywsLCwsLCwsLCwsLCwsDQosLEtleWJvYXJkcywsLCwsLCwsLCwsLCwsDQosLGRvY2tpbmcgc3RhdGlvbiwsLCwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpHYW1pbmcsLENvbnNvbGVzLCwsLCwsLCwsLCwsLCwNCiwsQWNjZXNzb3JpZXMsLCwsLCwsLCwsLCwsLA0KLCxHYW1lcywsLCwsLCwsLCwsLCwsDQosLENvdWxkIGluY2x1ZGUgTGVnbz8sLCwsLCwsLCwsLCwsLA==")));              const text = preloadedCSV;              const parsed = parseCSV(text);                            const rowsByName = {};              let filterStart = -1;              parsed.forEach((rc, i) => {                 if (rc && rc.length > 0 && rc[0]) rowsByName[rc[0]] = rc;                 if (rc && rc.length > 0 && rc[0] === 'Filter buttons') filterStart = i;              });                            const cols = [];              if(rowsByName['Roundel text']) {                const headerRow = rowsByName['Roundel text'];                for(let col = 1; col < headerRow.length; col++) {                   let label = headerRow[col];                   if (!label) continue;                                      let q = rowsByName['Search Query'] && rowsByName['Search Query'][col] ? rowsByName['Search Query'][col] : '';                   let img = rowsByName['Roundel image'] && rowsByName['Roundel image'][col] ? rowsByName['Roundel image'][col] : '';                   let ds = rowsByName['Discount Amount'] && rowsByName['Discount Amount'][col] ? rowsByName['Discount Amount'][col] : '';                   let pr = rowsByName['Price Range'] && rowsByName['Price Range'][col] ? rowsByName['Price Range'][col] : '';                   let rt = rowsByName['Retailer'] && rowsByName['Retailer'][col] ? rowsByName['Retailer'][col] : '';                   let ot = rowsByName['Offer Type'] && rowsByName['Offer Type'][col] ? rowsByName['Offer Type'][col] : '';                                      let filters = [];                   if(filterStart > 0) {                     for(let r = filterStart + 1; r < parsed.length; r++) {                         if(!parsed[r] || parsed[r][0] === 'Notes' || parsed[r][0] === 'Categories to consider') break;                         let f = parsed[r][col];                         if(f) filters.push(f);                     }                   }                   cols.push({ label, img, q, ds, pr, rt, ot, filters });                }              }              this.carouselData = cols;              if (this.carouselData && this.carouselData.length > 0) {                 const isMatched = this.carouselData.some(c => c.q === this.currentQuery || c.label === this.currentQuery);                 if (!isMatched) {                    const first = this.carouselData[0];                    this.currentQuery = first.q || first.label;                    if (this.priceFilter) this.priceFilter.value = 'all';                    if (this.customPriceMin) this.customPriceMin.value = '';                    if (this.customPriceMax) this.customPriceMax.value = '';                    let dPr = first.pr || 'all';                    if (typeof dPr === 'string' && dPr !== 'all') {                       let prLower = dPr.toLowerCase();                       if (prLower.includes('min') || prLower.includes('over')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else if (prLower.includes('max') || prLower.includes('under')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       }                    }                    let dAm = '0';                    if(first.ds && typeof first.ds === 'string') {                       let m = first.ds.match(/(\d+)/);                       if(m) dAm = m[1];                    }                    if (this.discountFilter) this.discountFilter.value = dAm;                    if (this.offerTypeSelect) this.offerTypeSelect.value = first.ot || '';                    if (this.retailerSelect) this.retailerSelect.value = first.rt || '';                    this.selectedBrands = [];                    if (this.brandDropdown) {                        const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                        chks.forEach(chk => chk.checked = false);                    }                    if (this.searchInput) this.searchInput.value = this.currentQuery;                 }              }              this.renderCarouselUI();          } catch(e){ console.warn(e); }        }                renderCarouselUI() {           const roundelWrapper = this.root.querySelector('.tg-df-carousel-roundels');           if(!roundelWrapper || !this.carouselData) return;                      let html = '';           this.carouselData.forEach(r => {              const q = r.q || r.label;              const isActive = (this.currentQuery === q || this.currentQuery === r.label) ? 'active' : '';              const imgHtml = r.img ? `\x3Cimg src="${r.img}" alt="${r.label}" />` : `\x3Csvg width="32" height="32" fill="#1F69FF" viewBox="0 0 24 24">\x3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\x3C/svg>`;              html += `                \x3Cdiv class="tg-df-roundel tg-df-carousel-cat ${isActive}" data-label="${this.escapeHTML(r.label)}">                  \x3Cdiv class="tg-df-roundel-img-box">${imgHtml}\x3C/div>                  \x3Cspan class="tg-df-roundel-label">${this.escapeHTML(r.label)}\x3C/span>                \x3C/div>              `;           });           roundelWrapper.innerHTML = html;                      // Rebind clicks           const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');           roundels.forEach(rNode => {             rNode.addEventListener('click', () => {                const r = this.carouselData.find(c => c.label === rNode.getAttribute('data-label'));                 if(!r) return;                                  if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: r.label,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = r.q || r.label;                const labelTitle = this.root.querySelector('#tg-df-carousel-title-label');                if (labelTitle) labelTitle.textContent = 'Best ' + this.currentQuery;                if (this.priceFilter) this.priceFilter.value = 'all';                if (this.customPriceMin) this.customPriceMin.value = '';                if (this.customPriceMax) this.customPriceMax.value = '';                let dPr = r.pr || 'all';                if (typeof dPr === 'string' && dPr !== 'all') {                   let prLower = dPr.toLowerCase();                   if (prLower.includes('min') || prLower.includes('over')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMin) this.customPriceMin.value = m[1];                   } else if (prLower.includes('max') || prLower.includes('under')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMax) this.customPriceMax.value = m[1];                   }                }                                let discountAmount = '0';                if(r.ds && typeof r.ds === 'string') {                   let m = r.ds.match(/(\d+)/);                   if(m) discountAmount = m[1];                }                if (this.discountFilter) this.discountFilter.value = discountAmount;                if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                                // Clear brands                    this.selectedBrands = [];                    if (this.brandDropdown) {                    const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                    chks.forEach(chk => chk.checked = false);                }                                if (this.searchInput) this.searchInput.value = this.currentQuery;                                roundels.forEach(ro => ro.classList.remove('active'));                if (rNode) rNode.classList.add('active');                                this.renderCarouselFilters(r);                this.fetchDeals(this.currentQuery);             });           });                      // Auto-highlight active           const activeR = this.carouselData.find(c => c.q === this.currentQuery || c.label === this.currentQuery);           if(activeR) this.renderCarouselFilters(activeR);        }                renderCarouselFilters(r) {           const filtersWrap = this.root.querySelector('.tg-df-carousel-filters-wrap');           if(!filtersWrap) return;                      let html = `\x3Cbutton class="tg-df-carousel-filter-btn" data-type="all">All\x3C/button>`;                      r.filters.forEach(f => {              let fL = f.toLowerCase();              let icon = '';              let logic = `data-type="custom" data-v="${this.escapeHTML(f)}"`;              if (fL === 'amazon deals' || fL === 'prime deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>`;              } else if (fL === 'lightning deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>`;              } else {                 if (fL.includes('lightning')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap">\x3Cpolygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2">\x3C/polygon>\x3C/svg>`;                 } else if (fL.includes('% off')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>`;                 } else if (fL.includes('under') || fL.includes('min ')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>`;                 }                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>${icon} ${this.escapeHTML(f)}\x3C/button>`;              }           });                      filtersWrap.innerHTML = html;                      const btns = filtersWrap.querySelectorAll('button');           btns.forEach(b => {             b.addEventListener('click', () => {                const type = b.getAttribute('data-type');                if (type === 'custom') {                   const v = b.getAttribute('data-v');                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-custom-${(v||'').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`, name: 'Custom Filter', label: v });                }                if (type === 'all') {                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: 'filter-clear-all', name: 'Clear all', label: 'Clear all filters' });                   // reset everything                   btns.forEach(btn => btn.classList.remove('active'));                   b.classList.add('active');                                      // Reset prices                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   if (this.brandDropdown) {                     const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                     chks.forEach(chk => chk.checked = false);                   }                } else {                   const v = b.getAttribute('data-v');                   const fL = v.toLowerCase();                                      let mapRet = ['amazon', 'walmart', 'best buy', 'target', 'john lewis', 'currys', 'argos'];                   const getCategory = (s) => {                      if (s === 'lightning deals' || s === 'amazon deals' || s === 'prime deals') return 'offer';                      if (s.includes('% off')) return 'discount';                      if (s.includes('under') || s.includes('over') || s.includes('min') || s.includes('max')) return 'price';                      if (mapRet.includes(s)) return 'retailer';                      return 'brand';                   };                   const cat = getCategory(fL);                   const wasActive = b.classList.contains('active');                   if (cat !== 'brand') {                      btns.forEach(btn => {                          if (btn === b) return;                          if (btn.getAttribute('data-type') === 'all') return;                          const bV = btn.getAttribute('data-v');                          if (!bV) return;                          if (getCategory(bV.toLowerCase()) === cat) btn.classList.remove('active');                      });                   }                   if (wasActive) b.classList.remove('active');                   else b.classList.add('active');                   let anyActive = Array.from(btns).some(btn => btn !== btns[0] && btn.classList.contains('active'));                   if (!anyActive) {                       btns[0].click();                       return;                   } else {                       btns[0].classList.remove('active');                   }                                      if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   btns.forEach(btn => {                       if (!btn.classList.contains('active') || btn.getAttribute('data-type') === 'all') return;                       const vv = btn.getAttribute('data-v');                       const vl = vv.toLowerCase();                                              if (vl === 'lightning deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_lightning';                       } else if (vl === 'amazon deals' || vl === 'prime deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_prime';                       } else if (vl.includes('% off')) {                          let m = vl.match(/(\d+)%/);                          if (m && this.discountFilter) this.discountFilter.value = m[1];                       } else if (vl.includes('under') || vl.includes('max')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       } else if (vl.includes('min') || vl.includes('over')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else {                          let foundR = mapRet.find(x => x === vl);                          if (foundR) {                             let realR = ['Amazon', 'Walmart', 'Best Buy', 'Target', 'John Lewis', 'Currys', 'Argos'].find(x => x.toLowerCase() === vl);                             if (this.retailerSelect) this.retailerSelect.value = realR;                          } else {                             this.selectedBrands.push(vv);                          }                       }                   });                                      if (this.brandDropdown) {                       const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                       chks.forEach(c => c.checked = this.selectedBrands.includes(c.value));                   }                                      if (r.pr && typeof r.pr === 'string') {                       let prL = r.pr.toLowerCase();                       if (prL.includes('under $')) {                           let m = prL.match(/under \$(\d+)/i);                           if (m && this.customPriceMax && !this.customPriceMax.value) this.customPriceMax.value = m[1];                       }                   }                }                                this.fetchDeals(this.currentQuery);             });           });                      // default to highlighting first           btns[0].classList.add('active');        }async fetchDeals(query, append = false) {          if (!append) {             this.showLoading();             this.deals = [];             this.displayLimit = this.getRowsLimit();          } else {             this.displayLimit += this.getRowsLimit();          }                    try {            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad(append);            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query, append);               } else {                 await this.fetchHawkDeals(query, append);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query, append);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const url = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let dynamicBrandsCounts = {};                    targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                   dynamicBrandsCounts[data.brand] = (dynamicBrandsCounts[data.brand] || 0) + 1;                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || ''                });             });          });                    const airedaleBrandsList = Object.keys(dynamicBrandsCounts).map(b => ({              formatted_value: b,              count: dynamicBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query, append = false) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (append && this.deals.length > 0) {            url.searchParams.append('offset', this.deals.length.toString());          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : (this.getViewMode() === 'savings_squad' ? 'date_desc' : 'discount_desc');          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin-top:8px;">${_div}                ${_div}              ${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)} (${this.airedaleTagCounts[tag] || 0})${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = isSavingsSquadMode ? 'View Deal' : 'Check Price';              if (isSavingsSquadMode) {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                  ${_div}                `;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = `                \x3Cdiv class="tg-df-card-merchant-wrapper">                  \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                ${_div}                \x3Cdiv class="tg-df-card-price-group">                  ${isSavingsSquadMode ? '' : `                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                  `}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description">\x3Cp>${this.escapeHTML(deal.description)}${_p}${_div}` : ''}                      ${_div}                    ${_div}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>${deal.isCheckPrice ? 'Check Price' : 'View Deal'}${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cp style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 12px; line-height: 1.4;">${this.escapeHTML(deal.description)}${_p}` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${priceGroupHtml}                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta ${isSavingsSquadMode ? 'tg-df-cta-savings-squad' : ''}" style="text-decoration: none;">${ctaText}${_a}                  ${_div}                ${_div}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit || ((this.getViewMode() === 'carousel' || this.getViewMode() === 'auto') && displayDeals.length > 0 && displayDeals.length % ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12) === 0)) {            if (this.getViewMode() === 'carousel') {               dealsHtml += `                 \x3Cbutton type="button" class="tg-df-load-more-card tg-df-load-more">                   \x3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;">\x3Cpath d="M5 12h14">\x3C/path>\x3Cpath d="m12 5 7 7-7 7">\x3C/path>\x3C/svg>                   Load More                 ${_button}               `;            } else {               dealsHtml += `                 \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                   \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer; display: flex; align-items: center;">Load More${_button}                 ${_div}               `;            }          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let rightChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             let leftChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-left');             if (this.getViewMode() === 'carousel') {                 // The observer set up in setupScrollListeners handles visibility.                 if (rightChevron) rightChevron.style.display = 'flex';                 if (leftChevron) leftChevron.style.display = 'none'; // reset correctly             } else {                 if (rightChevron) rightChevron.style.display = 'none';                 if (leftChevron) leftChevron.style.display = 'none';             }          }                    const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', async () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              if (displayDeals.length <= this.displayLimit) {                 loadMoreBtn.innerHTML = `                  <svg class="tg-df-spinner" style="width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 8px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/></svg>                  Loading...                 `;                 loadMoreBtn.disabled = true;                 await this.fetchDeals(this.currentQuery, true);              } else {                 this.displayLimit += this.getRowsLimit();                 this.render();              }            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            setTimeout(() => {               if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');               document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));            }, 50);          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Casabrews Ultra Pro review: Why don’t all espresso machines have a built-in timer? ]]></title>
                                                                                                <dc:content><![CDATA[ <p>When I tested the original <a href="https://www.tomsguide.com/home/coffee-makers/casabrews-ultra-review">Casabrews Ultra</a> last year, I was perplexed. It has a PID controller and a built-in timer for under $349? And it doesn’t look overly plasticky or feel flimsy? How? Some major boiler issues, that’s how. Thankfully, these issues have been sorted out on its successor, the Casabrews Ultra Pro, released in July 2026. </p><p>So is this enough to make it one of the <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">best espresso machines</a>? Hmm — not if you’re serious about your coffee. While it’s a good entry-level model with premium features like a customizable PID controller and has a built-in timer, its weak steam wand and habit of burning espresso holds me back from recommending it fully. </p><p>But might it be worth the $349 price tag? To get the full story, keep reading this Casabrews Ultra Pro review.</p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-specs"><span>Casabrews Ultra Pro review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/CASABREWS-Espresso-Machine-Heating-System/dp/B0H4FHZ2TZ" target="_blank" rel="nofollow">$349</a> </p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>Not specified </p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>12.2 x 10.3 x 13 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Grinder</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Heating system </strong></p></td><td  ><p>Casabrews proprietary</p></td></tr><tr><td class="firstcol " ><p><strong>Pressure </strong></p></td><td  ><p>20 bar</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank capacity</strong></p></td><td  ><p>34 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>Tamper, single shot filter (dual-walled and single-walled), double shot filter (dual walled and single-walled)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-price-availability"><span>Casabrews Ultra Pro review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="bRzVpvC3MmAePAG2s3chTF" name="Casabrews Ultra Pro 3 copy.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/bRzVpvC3MmAePAG2s3chTF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Casabrews Ultra Pro is <a href="https://www.amazon.com/CASABREWS-Espresso-Machine-Heating-System/dp/B0H4FHZ2TZ" target="_blank" rel="nofollow">$349 from Amazon U.S.</a> At time of writing, it’s not available in the U.K. </p><p>For a Casabrews machine, this is a pretty premium price. I mean, you can probably tell from the name, right? They don’t call it the “Ultra Pro” for no reason. I tested the <a href="https://www.tomsguide.com/home/coffee-makers/casabrews-ultra-review">original Ultra</a> last year and liked it, but it had some flaws, like a weak steam wand and boiler issues. More on that later, but I didn’t experience the same boiler problems. </p><p>The $349 asking price is a little too steep for this machine. When you can get the $299 <a href="https://www.tomsguide.com/home/coffee-makers/sage-breville-bambino-review">Breville Bambino</a>, PID controller and customizable pre-infusion included, or the $499 <a href="https://www.tomsguide.com/home/home-appliances/breville-sage-bambino-plus-review">Breville Bambino Plus</a>, there’s not a lot of wiggle room. </p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-design"><span>Casabrews Ultra Pro review: Design </span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Bf8G4ai2E3t2EmE5WQAddE" name="Casabrews Ultra Pro copy.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Bf8G4ai2E3t2EmE5WQAddE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The new Casabrews Ultra Pro looks pretty identical to the original Ultra. I liked the original version, so I like the new version. I didn’t notice any tarnishing issues this time around, so I’m glad to see Casabrews has improved on that; however, there’s still no water tank alert on the screen. </p><p>Speaking of the screen, I love it, and it’s by far the best feature on this machine. You don’t have to scrabble with a coffee scale timer anymore. Of course, you still need a coffee scale to ensure you’re getting the proper 36g espresso out, but forget that timer function. It’s redundant to you now. The Ultra Pro times your shot for you — and it’s reliable, every single time. </p><p>The screen has a few more capabilities: it shows you the double or single shot, hot water, and steam wand functions. That’s it, but I’m not mad about it. All I need is that sweet, sweet timer function, and I’m happy. </p><p>Like the previous model, the Ultra Pro utilizes a steam button rather than a dial, which is good if you need a low-effort machine. </p><p>Also like the previous machine, there’s a nice little groove on the top so you can store your tamper. I liked this feature as much as I like the 58mm portafilter basket. This is the coffee-snob standard, so is ideal for getting higher-quality aftermarket accessories… which you will need to purchase, as the Ultra Pro doesn’t come with a milk jug. Many Casabrews machines omit the milk jug, which I’m not a fan of. It’s so stingy. It’s literally a milk jug. All Breville machines come with a milk jug, but the $149 <a href="https://www.tomsguide.com/home/coffee-makers/delonghi-stilosa-review">De’Longhi Stilosa</a> doesn’t. </p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-espresso"><span>Casabrews Ultra Pro review: Espresso</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LcKr4CeXrwicUJsD7K2ZxF" name="Casabrews Ultra Pro 3.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/LcKr4CeXrwicUJsD7K2ZxF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I didn’t experience any boiler issues while using the Ultra Pro. On the previous version, it took ages for the machine to switch between milk and espresso. The Ultra Pro makes strange clicking noises after brewing espresso, but I was always able to get the steam wand up and running in a few seconds. </p><p>Despite these successes, I have a few espresso-related qualms with the Casabrews Ultra Pro. Casabrews claim “this espresso machine ensures consistent 9-bar extraction”, which isn’t true. I <em>know</em> it isn’t true because the Ultra Pro has an LCD screen that literally displays the bar pressure during extraction. </p><p>Not once did the bar pressure number stay the same. It fluctuated from 3 to 7 to 9 to 15 at one point, and down and up again throughout extraction. I know pressure is supposed to increase from low to 9-bar during infusion, but it’s not supposed to go back down when only 5g has extracted. </p><p>This is not a consistent 9-bar extraction. Casabrews kind of shot itself in the foot here. Why include a digital “manometer” (quotation marks necessary there) only for it to tell on itself? </p><p>Despite what Casabrews says on its website, there also doesn’t seem to be a solenoid valve, as all my pucks came out wet. Now I’ve grown accustomed to my Bambino Plus’s solenoid valve, I’m not sure I can ever go back. Cleanup is so much easier with dry pucks. </p><p>Here’s a photo of an espresso I made on the Casabrews Ultra Pro. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4061px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="PBTaydyjieQpKVR9MVRQZb" name="espresso casabrews" alt="a photo of coffee made on the casabrews ultra pro" src="https://cdn.mos.cms.futurecdn.net/PBTaydyjieQpKVR9MVRQZb.jpg" mos="" align="middle" fullscreen="" width="4061" height="2284" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>I wasn’t a huge fan of this espresso. It tasted burnt and overextracted, despite extracting in 25 seconds. This is the “ideal” extraction time. It didn’t pour too quickly or too slowly, meaning it wasn’t over- or under-extracted. </p><p>However, perhaps I’m simply too picky. I gave my colleagues many coffees made on the Casabrews Ultra Pro, and the feedback ranged from “really nice actually” to “a nice fruitiness”. </p><p>There’s also the (customizable) PID controller (temperature controller to prevent burnt or weak brews) to mention. This is better than Breville’s entry-level models, which have a non-programmable PID. You can easily adjust the brew temperature in intervals from 194°F to 204°F. This is great to see on a cheaper machine. </p><p>Finally, there’s a cold brew mode. Here’s a photo of said cold brew. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4474px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="95GHLnny8EjNSNbYKVFoBd" name="cold brew casabrews" alt="a photo of coffee made on the casabrews ultra pro" src="https://cdn.mos.cms.futurecdn.net/95GHLnny8EjNSNbYKVFoBd.jpg" mos="" align="middle" fullscreen="" width="4474" height="2517" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>The cold brew was bright and juicy, but quite astringent. It took 101 seconds to brew 160ml. I wouldn’t use this again, but it’s good if you like juicy, sharp flavors and you’re on a time crunch. </p><p>If you’re what you’d consider a “coffee snob”, this machine might not be the one for you. In that case, I’d recommend the aforementioned Breville Bambino or Breville Bambino Plus. You may even want to go for the $499 <a href="https://www.tomsguide.com/home/coffee-makers/gaggia-classic-e24-evo-pro-review">Gaggia Classic E24 Evo Pro</a>, which has no PID controller but is a coffee nerd goldmine. </p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-milk"><span>Casabrews Ultra Pro review: Milk</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="oAtvXgdhdidszGkCAzeDgF" name="Casabrews Ultra Pro 1 copy.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/oAtvXgdhdidszGkCAzeDgF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>My biggest issue with the Casabrews Ultra was the weak steam wand. Seeing as Casabrews fixed the boiler issues, I hoped the steam wand weakness would have been fixed, too. </p><p>Unfortunately, I was wrong. It still takes a whole minute to steam half a jug of milk, and said milk is unevenly aerated. I found my milk separated into layers immediately after finishing steaming. I also struggled to obtain the ideal “vortex” while steaming, which is the marker of a low-quality steam wand. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3949px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="6oySnhNaMetGww9WwzK8mb" name="casabrews milk" alt="a photo of coffee made on the casabrews ultra pro" src="https://cdn.mos.cms.futurecdn.net/6oySnhNaMetGww9WwzK8mb.jpg" mos="" align="middle" fullscreen="" width="3949" height="2222" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the latte art is squashed. This is because the foam wasn’t integrated into the milk, so when I poured, it separated into layers. This is not what I’d want to see. </p><p>The De’Longhi Stilosa, Breville Bambino, and Breville Bambino Plus don’t suffer from this issue, so I’d recommend grabbing one of those instead if you value latte art. Even the <a href="https://www.tomsguide.com/home/coffee-makers/ninja-luxe-cafe-mini-plus-review">Ninja Luxe Cafe Mini Plus</a> ($499)’s auto steam wand is better, if you can afford the extra $150 (but you get a grinder). </p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-storage-maintenance"><span>Casabrews Ultra Pro review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Z5dtEzXNRLPENkeE7Ldf8G" name="Casabrews Ultra Pro 6.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Z5dtEzXNRLPENkeE7Ldf8G.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Ultra Pro is just 13 inches deep and 12 inches tall, which makes it a mid-sized espresso machine. While considerably larger than the 7-inch-wide Breville Bambino and the 5-inch wide Casabrews 3700 Essential, it’s nowhere near as massive as the frankly Titanic <a href="https://www.tomsguide.com/home/coffee-makers/breville-oracle-dual-boiler-review">Breville Oracle Dual Boiler</a> ($3,000, 18 inches tall). </p><p>Obviously, still check it will fit in your kitchen, but it’s not a large model, as far as espresso machines go. </p><p>In terms of cleaning, well, Casabrews doesn’t provide you with any accessories, unlike Breville and De’Longhi. You’ll need to buy backflush tablets and descaler yourself, which is around <a href="https://www.amazon.com/Urnex-Espresso-Machine-Cleaning-Powder/dp/B001418KNS" target="_blank" rel="nofollow">$20 for a huge bottle on Amazon</a>.</p><p>In terms of warranty, it’s also pretty slim pickings here. Casabrews offers just one year, while Breville, De’Longhi, and Ninja offer two as standard.</p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-verdict"><span>Casabrews Ultra Pro review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aMg2SqGPfmx4jci48W5x5G" name="Casabrews Ultra Pro 1.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/aMg2SqGPfmx4jci48W5x5G.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I gave the Casabrews Ultra Pro 3 stars. Yeah, I know. “You gave the Ultra 3.5 stars, so how does that work?” The most egregious issue — the steam wand — has not been fixed. Even though Casabrews has had time to fix this, it didn’t, hence the lower rating this time around. Do I think the Ultra Pro is worth $349? No. No I do not. </p><p>When you can get the Casabrews CM5148 Pro for $199 — PID controller included — I really cannot see why the Ultra Pro is so much more expensive. The on-screen timer is a nice addition, but is it worth an extra $150? I think not. </p><p>So while I’m sure you won’t be majorly disappointed by the Ultra Pro, I simply can’t recommend it when there are similar machines for cheaper, or much better machines for a little extra. Treat yourself to the $499 Breville Bambino Plus or Gaggia Classic E24 Evo Pro, or save a little and get the Breville Bambino or Casabrews CM5418 Pro. It’s a no-brainer. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/casabrews-ultra-pro-review</link>
                                                                            <description>
                            <![CDATA[ The Casabrews Ultra Pro is a midrange espresso machine with a PID controller and a built-in timer, but the steam wand issues still haven’t been fixed. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">fYWvRUczdthpBiPWADxGL4</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/gd5oFXTPbjZpEGMLNUfB4F-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 13 Aug 2026 12:44:11 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/gd5oFXTPbjZpEGMLNUfB4F-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the casabrews ultra pro photographed against the blue tom&#039;s guide background]]></media:description>                                                            <media:text><![CDATA[the casabrews ultra pro photographed against the blue tom&#039;s guide background]]></media:text>
                                <media:title type="plain"><![CDATA[the casabrews ultra pro photographed against the blue tom&#039;s guide background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/gd5oFXTPbjZpEGMLNUfB4F-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>When I tested the original <a href="https://www.tomsguide.com/home/coffee-makers/casabrews-ultra-review">Casabrews Ultra</a> last year, I was perplexed. It has a PID controller and a built-in timer for under $349? And it doesn’t look overly plasticky or feel flimsy? How? Some major boiler issues, that’s how. Thankfully, these issues have been sorted out on its successor, the Casabrews Ultra Pro, released in July 2026. </p><p>So is this enough to make it one of the <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">best espresso machines</a>? Hmm — not if you’re serious about your coffee. While it’s a good entry-level model with premium features like a customizable PID controller and has a built-in timer, its weak steam wand and habit of burning espresso holds me back from recommending it fully. </p><p>But might it be worth the $349 price tag? To get the full story, keep reading this Casabrews Ultra Pro review.</p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-specs"><span>Casabrews Ultra Pro review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/CASABREWS-Espresso-Machine-Heating-System/dp/B0H4FHZ2TZ" target="_blank" rel="nofollow">$349</a> </p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>Not specified </p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>12.2 x 10.3 x 13 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Grinder</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Heating system </strong></p></td><td  ><p>Casabrews proprietary</p></td></tr><tr><td class="firstcol " ><p><strong>Pressure </strong></p></td><td  ><p>20 bar</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank capacity</strong></p></td><td  ><p>34 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>Tamper, single shot filter (dual-walled and single-walled), double shot filter (dual walled and single-walled)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-price-availability"><span>Casabrews Ultra Pro review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="bRzVpvC3MmAePAG2s3chTF" name="Casabrews Ultra Pro 3 copy.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/bRzVpvC3MmAePAG2s3chTF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Casabrews Ultra Pro is <a href="https://www.amazon.com/CASABREWS-Espresso-Machine-Heating-System/dp/B0H4FHZ2TZ" target="_blank" rel="nofollow">$349 from Amazon U.S.</a> At time of writing, it’s not available in the U.K. </p><p>For a Casabrews machine, this is a pretty premium price. I mean, you can probably tell from the name, right? They don’t call it the “Ultra Pro” for no reason. I tested the <a href="https://www.tomsguide.com/home/coffee-makers/casabrews-ultra-review">original Ultra</a> last year and liked it, but it had some flaws, like a weak steam wand and boiler issues. More on that later, but I didn’t experience the same boiler problems. </p><p>The $349 asking price is a little too steep for this machine. When you can get the $299 <a href="https://www.tomsguide.com/home/coffee-makers/sage-breville-bambino-review">Breville Bambino</a>, PID controller and customizable pre-infusion included, or the $499 <a href="https://www.tomsguide.com/home/home-appliances/breville-sage-bambino-plus-review">Breville Bambino Plus</a>, there’s not a lot of wiggle room. </p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-design"><span>Casabrews Ultra Pro review: Design </span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Bf8G4ai2E3t2EmE5WQAddE" name="Casabrews Ultra Pro copy.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Bf8G4ai2E3t2EmE5WQAddE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The new Casabrews Ultra Pro looks pretty identical to the original Ultra. I liked the original version, so I like the new version. I didn’t notice any tarnishing issues this time around, so I’m glad to see Casabrews has improved on that; however, there’s still no water tank alert on the screen. </p><p>Speaking of the screen, I love it, and it’s by far the best feature on this machine. You don’t have to scrabble with a coffee scale timer anymore. Of course, you still need a coffee scale to ensure you’re getting the proper 36g espresso out, but forget that timer function. It’s redundant to you now. The Ultra Pro times your shot for you — and it’s reliable, every single time. </p><p>The screen has a few more capabilities: it shows you the double or single shot, hot water, and steam wand functions. That’s it, but I’m not mad about it. All I need is that sweet, sweet timer function, and I’m happy. </p><p>Like the previous model, the Ultra Pro utilizes a steam button rather than a dial, which is good if you need a low-effort machine. </p><p>Also like the previous machine, there’s a nice little groove on the top so you can store your tamper. I liked this feature as much as I like the 58mm portafilter basket. This is the coffee-snob standard, so is ideal for getting higher-quality aftermarket accessories… which you will need to purchase, as the Ultra Pro doesn’t come with a milk jug. Many Casabrews machines omit the milk jug, which I’m not a fan of. It’s so stingy. It’s literally a milk jug. All Breville machines come with a milk jug, but the $149 <a href="https://www.tomsguide.com/home/coffee-makers/delonghi-stilosa-review">De’Longhi Stilosa</a> doesn’t. </p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-espresso"><span>Casabrews Ultra Pro review: Espresso</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LcKr4CeXrwicUJsD7K2ZxF" name="Casabrews Ultra Pro 3.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/LcKr4CeXrwicUJsD7K2ZxF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I didn’t experience any boiler issues while using the Ultra Pro. On the previous version, it took ages for the machine to switch between milk and espresso. The Ultra Pro makes strange clicking noises after brewing espresso, but I was always able to get the steam wand up and running in a few seconds. </p><p>Despite these successes, I have a few espresso-related qualms with the Casabrews Ultra Pro. Casabrews claim “this espresso machine ensures consistent 9-bar extraction”, which isn’t true. I <em>know</em> it isn’t true because the Ultra Pro has an LCD screen that literally displays the bar pressure during extraction. </p><p>Not once did the bar pressure number stay the same. It fluctuated from 3 to 7 to 9 to 15 at one point, and down and up again throughout extraction. I know pressure is supposed to increase from low to 9-bar during infusion, but it’s not supposed to go back down when only 5g has extracted. </p><p>This is not a consistent 9-bar extraction. Casabrews kind of shot itself in the foot here. Why include a digital “manometer” (quotation marks necessary there) only for it to tell on itself? </p><p>Despite what Casabrews says on its website, there also doesn’t seem to be a solenoid valve, as all my pucks came out wet. Now I’ve grown accustomed to my Bambino Plus’s solenoid valve, I’m not sure I can ever go back. Cleanup is so much easier with dry pucks. </p><p>Here’s a photo of an espresso I made on the Casabrews Ultra Pro. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4061px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="PBTaydyjieQpKVR9MVRQZb" name="espresso casabrews" alt="a photo of coffee made on the casabrews ultra pro" src="https://cdn.mos.cms.futurecdn.net/PBTaydyjieQpKVR9MVRQZb.jpg" mos="" align="middle" fullscreen="" width="4061" height="2284" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>I wasn’t a huge fan of this espresso. It tasted burnt and overextracted, despite extracting in 25 seconds. This is the “ideal” extraction time. It didn’t pour too quickly or too slowly, meaning it wasn’t over- or under-extracted. </p><p>However, perhaps I’m simply too picky. I gave my colleagues many coffees made on the Casabrews Ultra Pro, and the feedback ranged from “really nice actually” to “a nice fruitiness”. </p><p>There’s also the (customizable) PID controller (temperature controller to prevent burnt or weak brews) to mention. This is better than Breville’s entry-level models, which have a non-programmable PID. You can easily adjust the brew temperature in intervals from 194°F to 204°F. This is great to see on a cheaper machine. </p><p>Finally, there’s a cold brew mode. Here’s a photo of said cold brew. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4474px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="95GHLnny8EjNSNbYKVFoBd" name="cold brew casabrews" alt="a photo of coffee made on the casabrews ultra pro" src="https://cdn.mos.cms.futurecdn.net/95GHLnny8EjNSNbYKVFoBd.jpg" mos="" align="middle" fullscreen="" width="4474" height="2517" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>The cold brew was bright and juicy, but quite astringent. It took 101 seconds to brew 160ml. I wouldn’t use this again, but it’s good if you like juicy, sharp flavors and you’re on a time crunch. </p><p>If you’re what you’d consider a “coffee snob”, this machine might not be the one for you. In that case, I’d recommend the aforementioned Breville Bambino or Breville Bambino Plus. You may even want to go for the $499 <a href="https://www.tomsguide.com/home/coffee-makers/gaggia-classic-e24-evo-pro-review">Gaggia Classic E24 Evo Pro</a>, which has no PID controller but is a coffee nerd goldmine. </p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-milk"><span>Casabrews Ultra Pro review: Milk</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="oAtvXgdhdidszGkCAzeDgF" name="Casabrews Ultra Pro 1 copy.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/oAtvXgdhdidszGkCAzeDgF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>My biggest issue with the Casabrews Ultra was the weak steam wand. Seeing as Casabrews fixed the boiler issues, I hoped the steam wand weakness would have been fixed, too. </p><p>Unfortunately, I was wrong. It still takes a whole minute to steam half a jug of milk, and said milk is unevenly aerated. I found my milk separated into layers immediately after finishing steaming. I also struggled to obtain the ideal “vortex” while steaming, which is the marker of a low-quality steam wand. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3949px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="6oySnhNaMetGww9WwzK8mb" name="casabrews milk" alt="a photo of coffee made on the casabrews ultra pro" src="https://cdn.mos.cms.futurecdn.net/6oySnhNaMetGww9WwzK8mb.jpg" mos="" align="middle" fullscreen="" width="3949" height="2222" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the latte art is squashed. This is because the foam wasn’t integrated into the milk, so when I poured, it separated into layers. This is not what I’d want to see. </p><p>The De’Longhi Stilosa, Breville Bambino, and Breville Bambino Plus don’t suffer from this issue, so I’d recommend grabbing one of those instead if you value latte art. Even the <a href="https://www.tomsguide.com/home/coffee-makers/ninja-luxe-cafe-mini-plus-review">Ninja Luxe Cafe Mini Plus</a> ($499)’s auto steam wand is better, if you can afford the extra $150 (but you get a grinder). </p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-storage-maintenance"><span>Casabrews Ultra Pro review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Z5dtEzXNRLPENkeE7Ldf8G" name="Casabrews Ultra Pro 6.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Z5dtEzXNRLPENkeE7Ldf8G.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Ultra Pro is just 13 inches deep and 12 inches tall, which makes it a mid-sized espresso machine. While considerably larger than the 7-inch-wide Breville Bambino and the 5-inch wide Casabrews 3700 Essential, it’s nowhere near as massive as the frankly Titanic <a href="https://www.tomsguide.com/home/coffee-makers/breville-oracle-dual-boiler-review">Breville Oracle Dual Boiler</a> ($3,000, 18 inches tall). </p><p>Obviously, still check it will fit in your kitchen, but it’s not a large model, as far as espresso machines go. </p><p>In terms of cleaning, well, Casabrews doesn’t provide you with any accessories, unlike Breville and De’Longhi. You’ll need to buy backflush tablets and descaler yourself, which is around <a href="https://www.amazon.com/Urnex-Espresso-Machine-Cleaning-Powder/dp/B001418KNS" target="_blank" rel="nofollow">$20 for a huge bottle on Amazon</a>.</p><p>In terms of warranty, it’s also pretty slim pickings here. Casabrews offers just one year, while Breville, De’Longhi, and Ninja offer two as standard.</p><h2 class="article-body__section" id="section-casabrews-ultra-pro-review-verdict"><span>Casabrews Ultra Pro review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aMg2SqGPfmx4jci48W5x5G" name="Casabrews Ultra Pro 1.JPG" alt="the casabrews ultra pro photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/aMg2SqGPfmx4jci48W5x5G.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I gave the Casabrews Ultra Pro 3 stars. Yeah, I know. “You gave the Ultra 3.5 stars, so how does that work?” The most egregious issue — the steam wand — has not been fixed. Even though Casabrews has had time to fix this, it didn’t, hence the lower rating this time around. Do I think the Ultra Pro is worth $349? No. No I do not. </p><p>When you can get the Casabrews CM5148 Pro for $199 — PID controller included — I really cannot see why the Ultra Pro is so much more expensive. The on-screen timer is a nice addition, but is it worth an extra $150? I think not. </p><p>So while I’m sure you won’t be majorly disappointed by the Ultra Pro, I simply can’t recommend it when there are similar machines for cheaper, or much better machines for a little extra. Treat yourself to the $499 Breville Bambino Plus or Gaggia Classic E24 Evo Pro, or save a little and get the Breville Bambino or Casabrews CM5418 Pro. It’s a no-brainer. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested the Keurig K-Crema, and it’s my new go-to for fancy coffee drinks ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">Keurig K-Crema review: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price: </strong>$219.99<br><strong>Dimensions: </strong>13.3 x 8.1 x 13.1 inches<br><strong>Reservoir capacity: </strong>72 ounces<br><strong>Brew sizes: </strong>Single shot, double shot, 6, 8, 10, or 12 ounces<br><strong>Strength control: </strong>No<br><strong>Features: </strong>Height-adjustable drip tray, High Altitude Brewing Mode<br><strong>Colors: </strong>Black</p></div></div><p>Ever since my dad got a fancy espresso machine, I’ve dreamed of brewing my own crema-topped coffee at home. But most at-home, <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers">coffee makers</a> don’t offer this feature — unless you want to pay hundreds of dollars and sacrifice a large chunk of your kitchen’s counter space. </p><p>When I saw that Keurig was coming out with the K-Crema, I wondered if it could be a good lower-cost option to my dad’s top-of-the-line model.</p><p>Ultimately, the K-Crema brews a tasty cup of coffee with a layer of foamy crema on top. It might not stack up to an espresso shot from a pricier machine or a coffee shop, but for those of us who want to save money while still enjoying a delicious shot of coffee, the K-Crema is a decent pick.</p><p>Read this Keurig K-Crema review to find out how it performed over several weeks in my kitchen, and why I think this is a top pick in the <a href="https://www.tomsguide.com/best-picks/the-best-keurig-coffee-makers">Keurig coffee maker</a> lineup.</p><h3 class="article-body__section" id="section-keurig-k-crema-review-price-and-availability"><span>Keurig K-Crema review: Price and availability</span></h3><p>The Keurig K-Crema is available for $219 at <a href="https://www.keurig.com/K-Crema%E2%84%A2-High-Pressure-Coffee-Maker/p/K-Crema-Coffee-Maker">Keurig</a> and <a href="https://www.amazon.com/dp/B0FFW5169X">Amazon</a>. You can also buy it at <a href="https://www.target.com/p/keurig-k-crema-single-serve-k-cup-pod-coffee-maker/-/A-94682461"><u>Target</u></a>, <a href="https://www.kohls.com/product/prd-7882845/keurig-k-crema-single-serve-coffee-maker-with-4-cup-2-shot-sizes.jsp"><u>Kohl’s</u></a>, <a href="https://www.walmart.com/ip/KEURIG-K-CREMA/15928404601"><u>Walmart</u></a>, and other major retailers. Plus, the machine is available in a single color: Black.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="H95sAzyz3snVQqLzFisxbB" name="Keurig K-Crema-3" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/H95sAzyz3snVQqLzFisxbB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You can use any K-Cup pod in the K-Crema, though mine came with Global Trek medium roast pods. The machine doesn’t come with a reusable pod, but you could buy one and use your own coffee if you prefer. For this review, I stuck with the provided K-Cups.</p><h3 class="article-body__section" id="section-keurig-k-crema-review-design"><span>Keurig K-Crema review: Design</span></h3><p>After initially unboxing the Keurig K-Crema, the first thing I noticed was its size. It’s bigger than your basic Keurig machine, like the K-Slim or the K-Mini, yet smaller than most coffee machines. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qM7BpivXF9LF7vUkhYmjfB" name="Keurig K-Crema-5" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/qM7BpivXF9LF7vUkhYmjfB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The K-Crema has a removable water reservoir that sits to the right of the machine, making it wider than some other single-brew coffee machines. But the reservoir was easy to fill: simply pick it up using the handle, fill it with water, and pop it back.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="f44KxQM9mqVCpVEW5ntqWB" name="Keurig K-Crema-4" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/f44KxQM9mqVCpVEW5ntqWB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The K-Crema has six size options, from a single shot to a 12-ounce brew. The “Pressure” button brews coffee at five times the pressure of a standard cup, resulting in a silky layer of crema. That’s what sets the machine apart from other Keurigs, but can it justify the higher price tag?</p><h3 class="article-body__section" id="section-keurig-k-crema-review-performance"><span>Keurig K-Crema review: Performance</span></h3><p>To determine whether or not the Keurig K-Crema is a good buy, I put it to the test in my kitchen over several weeks. I timed each drink setting and took the temperature of multiple cups to gauge the average hotness of a cup of coffee.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="yAB3wLNKCpqHBacCdhmfgB" name="Keurig K-Crema-7" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/yAB3wLNKCpqHBacCdhmfgB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Here’s how the Keurig K-Crema performed:</p><div ><table><tbody><tr><td class="firstcol " ><p><strong>Cup size</strong></p></td><td  ><p><strong>Time for classic brew</strong></p></td><td  ><p><strong>Time for pressure brew</strong></p></td><td  ><p><strong>Temperature in Fahrenheit</strong></p></td></tr><tr><td class="firstcol " ><p><strong>Shot</strong></p></td><td  ><p>N/A</p></td><td  ><p>41 seconds</p></td><td  ><p>163 degrees</p></td></tr><tr><td class="firstcol " ><p><strong>Double shot</strong></p></td><td  ><p>N/A</p></td><td  ><p>51 seconds</p></td><td  ><p>168 degrees</p></td></tr><tr><td class="firstcol " ><p><strong>6 ounces</strong></p></td><td  ><p>1 minute 23 seconds</p></td><td  ><p>1 minute 2 seconds</p></td><td  ><p>171 degrees</p></td></tr><tr><td class="firstcol " ><p><strong>8 ounces</strong></p></td><td  ><p>1 minute 31 seconds</p></td><td  ><p>1 minute 11 seconds</p></td><td  ><p>170 degrees</p></td></tr><tr><td class="firstcol " ><p><strong>10 ounces</strong></p></td><td  ><p>1 minute 50 seconds</p></td><td  ><p>1 minute 23 seconds</p></td><td  ><p>174 degrees</p></td></tr><tr><td class="firstcol " ><p><strong>12 ounces</strong></p></td><td  ><p>2 minutes 8 seconds</p></td><td  ><p>1 minute 36 seconds</p></td><td  ><p>173 degrees</p></td></tr></tbody></table></div><p>These brew times are comparable to other Keurig machines, though it was interesting to note that the machine brewed faster when I used the pressure setting. Coffee temperature was a little colder than I expected, especially for the shot and double-shot sizes. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AQu7MeSuJKE8GqxFEK55gB" name="Keurig K-Crema-6" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/AQu7MeSuJKE8GqxFEK55gB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I measured brew temps from a low of 163 degrees to a high of 174 degrees. Experts recommend a water temperature of 195 to 205 degrees for the best-testing coffee, but that’s not realistic from a single-serve machine like the Keurig K-Crema.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RA5jwMLXgT8suPv8A9DbWB" name="Keurig K-Crema-8" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/RA5jwMLXgT8suPv8A9DbWB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Noise-wise, I measured a high of 68 decibels for both the Classic and Pressure settings, with an average in the 50 to 60 decibel range. That’s considered to be in the moderate range — and since the machine brews most sizes in under two minutes, it’s unlikely to cause much disruption.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9EMJTncM8VqAZ8cc9pmsaB" name="Keurig K-Crema-9" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/9EMJTncM8VqAZ8cc9pmsaB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Now for the most important factor: taste. My only prior experience of a crema machine is in my dad’s kitchen, where he has a fancy (and expensive) espresso machine. I didn’t expect the K-Crema to measure up, yet I was still pleasantly surprised by the taste of the resulting cup. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2XUxJHNAJt3sgZMkrNAnfB" name="Keurig K-Crema-10" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/2XUxJHNAJt3sgZMkrNAnfB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>In the past, I’ve found K-Cups to taste too weak or artificial, but the Global Trek pods I tested were just right. I liked the addition of the crema, though it didn’t quite hold up when I mixed in milk and sugar. </p><h3 class="article-body__section" id="section-keurig-k-crema-review-comparison"><span>Keurig K-Crema review: Comparison</span></h3><p>The Keurig K-Crema is a solid single-serve coffee maker, but how does it compare to other Keurig models? I have previously tested the K-Mini Mate, and the K-Crema definitely brews a superior cup. But it comes at a cost: The <a href="https://www.tomsguide.com/home/coffee-makers/keurig-k-mini-mate-coffee-maker-review"><u>Keurig K-Mini Mate</u></a> costs $59.99, compared to $219.99 for the K-Crema.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="WeRshmU3pxkz3w5HcPV2kB" name="Keurig K-Crema-12" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/WeRshmU3pxkz3w5HcPV2kB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You might also consider the <a href="https://www.tomsguide.com/reviews/keurig-k-cafe"><u>Keurig K-Cafe</u></a>, which comes with a milk frother. This machine is ideal if you like coffee drinks, like lattes and cappuccinos. But if you prefer to drink straight shots of espresso, the K-Crema is the right choice.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rxjRYLQzA4fNskvVXpKjiB" name="Keurig K-Crema-11" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/rxjRYLQzA4fNskvVXpKjiB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Ultimately, you’ll want to consider what you want out of your coffee machine. If you’ve always wanted an espresso machine but don’t want to spend the money, the Keurig K-Crema is a good budget-friendly option.</p><h3 class="article-body__section" id="section-keurig-k-crema-review-verdict"><span>Keurig K-Crema review: Verdict</span></h3><p>If you’re looking for an affordable coffee maker that can brew a decent cup of coffee or a shot of creamy espresso, I recommend looking into the Keurig K-Crema. While it doesn’t quite measure up to the expensive espresso machines on the market, it’s available for a fraction of the price, and the resulting coffee is smooth and enjoyable. </p><p>It’s the ideal middle ground for someone who wants to brew coffee drinks at home, but isn’t ready to commit to an espresso machine.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/coffee-makers/keurig-k-crema-review</link>
                                                                            <description>
                            <![CDATA[ The Keurig K-Crema is easy to use and can brew a shot in under a minute. It’s a shame that the taste could be better. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">fKzPeQzEuLCn48EZczurKL</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/DBgFUtLFv7DX6onacw5tdB-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 12 Aug 2026 05:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                                    <dc:creator><![CDATA[ Catherine Hiles ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/ehjj3r2KFzMLw5yobwSyrV.png ]]></dc:source>
                                                                <dc:description><![CDATA[ null ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/DBgFUtLFv7DX6onacw5tdB-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Keurig K-Crema coffee maker on a kitchen counter]]></media:description>                                                            <media:text><![CDATA[Keurig K-Crema coffee maker on a kitchen counter]]></media:text>
                                <media:title type="plain"><![CDATA[Keurig K-Crema coffee maker on a kitchen counter]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/DBgFUtLFv7DX6onacw5tdB-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">Keurig K-Crema review: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price: </strong>$219.99<br><strong>Dimensions: </strong>13.3 x 8.1 x 13.1 inches<br><strong>Reservoir capacity: </strong>72 ounces<br><strong>Brew sizes: </strong>Single shot, double shot, 6, 8, 10, or 12 ounces<br><strong>Strength control: </strong>No<br><strong>Features: </strong>Height-adjustable drip tray, High Altitude Brewing Mode<br><strong>Colors: </strong>Black</p></div></div><p>Ever since my dad got a fancy espresso machine, I’ve dreamed of brewing my own crema-topped coffee at home. But most at-home, <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers">coffee makers</a> don’t offer this feature — unless you want to pay hundreds of dollars and sacrifice a large chunk of your kitchen’s counter space. </p><p>When I saw that Keurig was coming out with the K-Crema, I wondered if it could be a good lower-cost option to my dad’s top-of-the-line model.</p><p>Ultimately, the K-Crema brews a tasty cup of coffee with a layer of foamy crema on top. It might not stack up to an espresso shot from a pricier machine or a coffee shop, but for those of us who want to save money while still enjoying a delicious shot of coffee, the K-Crema is a decent pick.</p><p>Read this Keurig K-Crema review to find out how it performed over several weeks in my kitchen, and why I think this is a top pick in the <a href="https://www.tomsguide.com/best-picks/the-best-keurig-coffee-makers">Keurig coffee maker</a> lineup.</p><h3 class="article-body__section" id="section-keurig-k-crema-review-price-and-availability"><span>Keurig K-Crema review: Price and availability</span></h3><p>The Keurig K-Crema is available for $219 at <a href="https://www.keurig.com/K-Crema%E2%84%A2-High-Pressure-Coffee-Maker/p/K-Crema-Coffee-Maker">Keurig</a> and <a href="https://www.amazon.com/dp/B0FFW5169X">Amazon</a>. You can also buy it at <a href="https://www.target.com/p/keurig-k-crema-single-serve-k-cup-pod-coffee-maker/-/A-94682461"><u>Target</u></a>, <a href="https://www.kohls.com/product/prd-7882845/keurig-k-crema-single-serve-coffee-maker-with-4-cup-2-shot-sizes.jsp"><u>Kohl’s</u></a>, <a href="https://www.walmart.com/ip/KEURIG-K-CREMA/15928404601"><u>Walmart</u></a>, and other major retailers. Plus, the machine is available in a single color: Black.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="H95sAzyz3snVQqLzFisxbB" name="Keurig K-Crema-3" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/H95sAzyz3snVQqLzFisxbB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You can use any K-Cup pod in the K-Crema, though mine came with Global Trek medium roast pods. The machine doesn’t come with a reusable pod, but you could buy one and use your own coffee if you prefer. For this review, I stuck with the provided K-Cups.</p><h3 class="article-body__section" id="section-keurig-k-crema-review-design"><span>Keurig K-Crema review: Design</span></h3><p>After initially unboxing the Keurig K-Crema, the first thing I noticed was its size. It’s bigger than your basic Keurig machine, like the K-Slim or the K-Mini, yet smaller than most coffee machines. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qM7BpivXF9LF7vUkhYmjfB" name="Keurig K-Crema-5" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/qM7BpivXF9LF7vUkhYmjfB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The K-Crema has a removable water reservoir that sits to the right of the machine, making it wider than some other single-brew coffee machines. But the reservoir was easy to fill: simply pick it up using the handle, fill it with water, and pop it back.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="f44KxQM9mqVCpVEW5ntqWB" name="Keurig K-Crema-4" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/f44KxQM9mqVCpVEW5ntqWB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The K-Crema has six size options, from a single shot to a 12-ounce brew. The “Pressure” button brews coffee at five times the pressure of a standard cup, resulting in a silky layer of crema. That’s what sets the machine apart from other Keurigs, but can it justify the higher price tag?</p><h3 class="article-body__section" id="section-keurig-k-crema-review-performance"><span>Keurig K-Crema review: Performance</span></h3><p>To determine whether or not the Keurig K-Crema is a good buy, I put it to the test in my kitchen over several weeks. I timed each drink setting and took the temperature of multiple cups to gauge the average hotness of a cup of coffee.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="yAB3wLNKCpqHBacCdhmfgB" name="Keurig K-Crema-7" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/yAB3wLNKCpqHBacCdhmfgB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Here’s how the Keurig K-Crema performed:</p><div ><table><tbody><tr><td class="firstcol " ><p><strong>Cup size</strong></p></td><td  ><p><strong>Time for classic brew</strong></p></td><td  ><p><strong>Time for pressure brew</strong></p></td><td  ><p><strong>Temperature in Fahrenheit</strong></p></td></tr><tr><td class="firstcol " ><p><strong>Shot</strong></p></td><td  ><p>N/A</p></td><td  ><p>41 seconds</p></td><td  ><p>163 degrees</p></td></tr><tr><td class="firstcol " ><p><strong>Double shot</strong></p></td><td  ><p>N/A</p></td><td  ><p>51 seconds</p></td><td  ><p>168 degrees</p></td></tr><tr><td class="firstcol " ><p><strong>6 ounces</strong></p></td><td  ><p>1 minute 23 seconds</p></td><td  ><p>1 minute 2 seconds</p></td><td  ><p>171 degrees</p></td></tr><tr><td class="firstcol " ><p><strong>8 ounces</strong></p></td><td  ><p>1 minute 31 seconds</p></td><td  ><p>1 minute 11 seconds</p></td><td  ><p>170 degrees</p></td></tr><tr><td class="firstcol " ><p><strong>10 ounces</strong></p></td><td  ><p>1 minute 50 seconds</p></td><td  ><p>1 minute 23 seconds</p></td><td  ><p>174 degrees</p></td></tr><tr><td class="firstcol " ><p><strong>12 ounces</strong></p></td><td  ><p>2 minutes 8 seconds</p></td><td  ><p>1 minute 36 seconds</p></td><td  ><p>173 degrees</p></td></tr></tbody></table></div><p>These brew times are comparable to other Keurig machines, though it was interesting to note that the machine brewed faster when I used the pressure setting. Coffee temperature was a little colder than I expected, especially for the shot and double-shot sizes. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AQu7MeSuJKE8GqxFEK55gB" name="Keurig K-Crema-6" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/AQu7MeSuJKE8GqxFEK55gB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I measured brew temps from a low of 163 degrees to a high of 174 degrees. Experts recommend a water temperature of 195 to 205 degrees for the best-testing coffee, but that’s not realistic from a single-serve machine like the Keurig K-Crema.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RA5jwMLXgT8suPv8A9DbWB" name="Keurig K-Crema-8" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/RA5jwMLXgT8suPv8A9DbWB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Noise-wise, I measured a high of 68 decibels for both the Classic and Pressure settings, with an average in the 50 to 60 decibel range. That’s considered to be in the moderate range — and since the machine brews most sizes in under two minutes, it’s unlikely to cause much disruption.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9EMJTncM8VqAZ8cc9pmsaB" name="Keurig K-Crema-9" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/9EMJTncM8VqAZ8cc9pmsaB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Now for the most important factor: taste. My only prior experience of a crema machine is in my dad’s kitchen, where he has a fancy (and expensive) espresso machine. I didn’t expect the K-Crema to measure up, yet I was still pleasantly surprised by the taste of the resulting cup. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2XUxJHNAJt3sgZMkrNAnfB" name="Keurig K-Crema-10" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/2XUxJHNAJt3sgZMkrNAnfB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>In the past, I’ve found K-Cups to taste too weak or artificial, but the Global Trek pods I tested were just right. I liked the addition of the crema, though it didn’t quite hold up when I mixed in milk and sugar. </p><h3 class="article-body__section" id="section-keurig-k-crema-review-comparison"><span>Keurig K-Crema review: Comparison</span></h3><p>The Keurig K-Crema is a solid single-serve coffee maker, but how does it compare to other Keurig models? I have previously tested the K-Mini Mate, and the K-Crema definitely brews a superior cup. But it comes at a cost: The <a href="https://www.tomsguide.com/home/coffee-makers/keurig-k-mini-mate-coffee-maker-review"><u>Keurig K-Mini Mate</u></a> costs $59.99, compared to $219.99 for the K-Crema.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="WeRshmU3pxkz3w5HcPV2kB" name="Keurig K-Crema-12" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/WeRshmU3pxkz3w5HcPV2kB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You might also consider the <a href="https://www.tomsguide.com/reviews/keurig-k-cafe"><u>Keurig K-Cafe</u></a>, which comes with a milk frother. This machine is ideal if you like coffee drinks, like lattes and cappuccinos. But if you prefer to drink straight shots of espresso, the K-Crema is the right choice.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rxjRYLQzA4fNskvVXpKjiB" name="Keurig K-Crema-11" alt="Keurig K-Crema coffee maker on a kitchen counter" src="https://cdn.mos.cms.futurecdn.net/rxjRYLQzA4fNskvVXpKjiB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Ultimately, you’ll want to consider what you want out of your coffee machine. If you’ve always wanted an espresso machine but don’t want to spend the money, the Keurig K-Crema is a good budget-friendly option.</p><h3 class="article-body__section" id="section-keurig-k-crema-review-verdict"><span>Keurig K-Crema review: Verdict</span></h3><p>If you’re looking for an affordable coffee maker that can brew a decent cup of coffee or a shot of creamy espresso, I recommend looking into the Keurig K-Crema. While it doesn’t quite measure up to the expensive espresso machines on the market, it’s available for a fraction of the price, and the resulting coffee is smooth and enjoyable. </p><p>It’s the ideal middle ground for someone who wants to brew coffee drinks at home, but isn’t ready to commit to an espresso machine.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Dualit Design Series Kettle review ]]></title>
                                                                                                <dc:content><![CDATA[ <p>As a tea-drinking Brit, I have big opinions on kettles, and the Dualit Design Series Kettle is a good option for most people’s homes. It has an aesthetic stainless steel design which is easy to maintain with regular cleaning. It has good heat retention and is easy and accurate to pour with. </p><p>Unfortunately, though, the window design makes it hard to read the measurements when filling the kettle. The boiling is also a little slow when compared to other kettles. </p><p>To find out more about the kitchen appliance, you can keep reading this full Dualit Design Series Kettle review. </p><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-specs"><span>Dualit Design Series Kettle review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Dualit-72955-Design-Kettle-Black/dp/B00UHKSRMG" target="_blank" rel="nofollow">$178</a></p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>1.5L</p></td></tr><tr><td class="firstcol " ><p><strong>Rated power</strong></p></td><td  ><p>1.5kW</p></td></tr><tr><td class="firstcol " ><p><strong>Exterior material</strong></p></td><td  ><p>Stainless steel</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>9.4 x 8.6 x 5.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>3.2 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Features</strong></p></td><td  ><p>Interchangeable panels, Boil-dry protection, Pure pour spout</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-price-availability"><span>Dualit Design Series Kettle review: Price & availability</span></h2><p>The Dualit Design Series Kettle is available to purchase from <a href="https://www.amazon.com/Dualit-72955-Design-Kettle-Black/dp/B00UHKSRMG" target="_blank" rel="nofollow">Amazon U.S. for $178</a> or directly from the <a href="https://us.dualit.com/products/design-series-kettle" target="_blank" rel="nofollow">Dualit online store for $179</a>. You can also purchase the kettle in the U.K., where it’s called the <a href="https://www.tomsguide.com/reviews/dualit-architect-kettle">Dualit Architect Kettle</a>. </p><p>The kettle has a stainless steel finish, but you can purchase <a href="https://www.dualit.com/products/architect-kettle-panels" target="_blank" rel="nofollow">interchangeable panels </a>for the bottom section from the Dualit store to make it your own. These include the colors Shadow, Metallic Silver, Copper, Brass, Evergreen and Eucalyptus. </p><p>Dualit also sells the <a href="https://www.tomsguide.com/home/home-appliances/dualit-classic-kettle-review">Classic Kettle</a> for $249, which is more of a fashion statement than a standard kettle. Its curved shape is much more interesting than the Design Series — the irony is not lost on me.</p><p>But you don’t need to spend the best part of $200 for a good kettle. I have also tested the <a href="https://www.tomsguide.com/home/home-appliances/dualit-classic-kettle-review">Cosori Electric Glass Kettle</a>, which, while not as dashing, is a fraction of the price at just $27. And honestly, it does just as good a job as the Design Series Kettle — you just have to be willing to put up with the odd leak while pouring, which isn’t a worry with the Dualit.</p><p>While the Dualit has a Pure pour spout to ensure zero leaks when pouring, if you need even more precision for drinks such as pour-over coffee, you may want to consider a gooseneck kettle such as the <a href="https://www.tomsguide.com/home/home-appliances/fellow-stagg-ekg-kettle-review">Fellow Stagg EKG</a> for $165. If you take your brews seriously, I’d recommend the Fellow over the Dualit.</p><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-design-features"><span>Dualit Design Series Kettle review: Design & features</span></h2><p>The Dualit Design Series Kettle is certainly eye-catching. It measures 9.4 x 8.6 x 5.5 inches, weighs 3.2 pounds, and has a reflective surface that will have you checking out your hair while the water boils. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vQQ7jxTzikipJHUeGEbBHW" name="Dualit Design Series Kettle" alt="Dualit Design Series Kettle photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/vQQ7jxTzikipJHUeGEbBHW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>It can boil a maximum capacity of 1.5 liters, enough for six cups. However, you’ll find yourself playing a guessing game when filling it because of the design and positioning of the measurement window. The window has a fish-eye effect, warping the water as you peer through, and it sits directly in line with the handle, so you need to look around that too. </p><p>The handle extends down most of the body, making it easy to hold. At the top of the handle there’s a button to release the flat, hinged lid. This means you can open it without touching it, but to close it you’ll need to push it down. If you’ve already boiled the kettle once, be wary as this part can get very hot. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ugUvCUdXy2Q2SLczdobUiW" name="Dualit Design Series Kettle" alt="Dualit Design Series Kettle photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/ugUvCUdXy2Q2SLczdobUiW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The power switch is just below the handle at the base. This base is very cohesive with the rest of the kettle — it looks like one whole unit with the kettle on top. It has a three-foot power cord, so it is best set up near a power outlet. If you need to make this shorter, you can store the excess in the base.</p><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-performance"><span>Dualit Design Series Kettle review: Performance </span></h2><p>I have to say, when testing the Dualit Design Series Kettle, I was a little confused. After testing the Dualit Classic Kettle, I was impressed at how fast it boiled the water. It boiled five cups in just two minutes and 35 seconds. The Design Series Kettle took three minutes and 11 seconds — a stark difference. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3TE8WQrPykucFMwxWgcVwW" name="Dualit Design Series Kettle" alt="Dualit Design Series Kettle photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/3TE8WQrPykucFMwxWgcVwW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>All of my testing conditions were the same, so if you want an appliance that won’t have you waiting around all day, spring for the more expensive Classic. You may be spending a little extra, but hey, you’ll get to enjoy life with your fresh brew instead of staring into your lifeless eyes in the kettle’s reflection as the water boils for an extra minute. Just me? Well, as a tea-drinking Brit, by the time I’ve made my daily quota, all of those minutes add up.</p><div ><table><tbody><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p><strong>Boiling time</strong></p></td></tr><tr><td class="firstcol " ><p><strong>2 cups</strong></p></td><td  ><p>1 minute, 35 seconds</p></td></tr><tr><td class="firstcol " ><p><strong>3 cups</strong></p></td><td  ><p>2 minutes, 5 seconds</p></td></tr><tr><td class="firstcol " ><p><strong>4 cups </strong></p></td><td  ><p>2 minutes, 41 seconds</p></td></tr><tr><td class="firstcol " ><p><strong>5 cups </strong></p></td><td  ><p>3 minutes, 11 seconds</p></td></tr><tr><td class="firstcol " ><p><strong>Max</strong></p></td><td  ><p>3 minutes, 53 seconds</p></td></tr></tbody></table></div><p>I measured the temperature of the water immediately after boiling and got a reading of 209.3ºF/98.5ºC. This was very slightly cooler than the 210ºF/99.3ºC reading I got from the Classic Kettle. But the Design Series holds heat pretty well. After an hour, I got a reading of 161.1ºF/71.7ºC. So, if you wanted to reboil the water, you won’t be waiting for the full *checks notes* three minutes and 53 seconds — yikes. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ipma4RqBU5Rb5HruMitjYW" name="Dualit Design Series Kettle" alt="Dualit Design Series Kettle photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/ipma4RqBU5Rb5HruMitjYW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Pouring with the kettle is a dream. If you’re a little shaky when pouring, I would fully recommend this kettle. I encountered no spillages. And the water went exactly where I needed it to. It can be a little heavy when you fill it to the top, so I only ever filled it about halfway so I could still use it with ease. </p><p>The kettle isn’t too loud either, and gave me a reading of 76dB on the Decibel X app.</p><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-storage-maintenance"><span>Dualit Design Series Kettle review: Storage & maintenance </span></h2><p>The Dualit Design Series is a kettle, and as such, this is a generally straightforward affair. It’s easy to maintain; you’ll just find yourself wiping down the exterior more than your usual kettle given its reflective nature. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iyrLxfLyPwcSnTSiRmWXuW" name="Dualit Design Series Kettle" alt="Dualit Design Series Kettle photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/iyrLxfLyPwcSnTSiRmWXuW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you live in a hard water area, you’ll also need to descale the kettle regularly. You can purchase descaling tablets for this or use natural <a href="https://www.tomsguide.com/home/how-to-clean-a-kettle-7-simple-steps">methods such as lemon juice or baking soda</a>. The spout of the kettle also has a removable limescale filter which is easy to remove, clean, and refit. </p><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-verdict"><span>Dualit Design Series Kettle review: Verdict</span></h2><p>The Dualit Design Series Kettle is undeniably good-looking. The stainless steel finish is slick and will fit in with any kitchen. The kettle is also easy to maintain (if you don’t mind wiping it down a lot). It has good heat retention and is very easy to pour with. There were absolutely zero drips thanks to the Pure pour spout. </p><p>Unfortunately, it’s tricky to tell where the water has been filled up to because of the poor window design, and boiling is a little slow. It took a whole minute longer to boil the same volume of water as the Dualit Classic Kettle.</p><p>If sleek looks are your priority, this is a great kettle, but $178 is a lot of money to spend on a kettle that takes so long to boil. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/dualit-design-series-kettle-review</link>
                                                                            <description>
                            <![CDATA[ The Dualit Design Series Kettle is eye-catching and prevents spills, but you may be waiting a while for it to boil. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">cXwSrBEZW5zGvnAtYzrL3k</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/boLRuFNtDaDzwRwUeNtDAW-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 07 Aug 2026 15:00:39 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ ashley.thieme@futurenet.com (Ashley Thieme) ]]></author>                    <dc:creator><![CDATA[ Ashley Thieme ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3AWovHjApwuNrSGRS6WBcL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ashley Thieme is a staff writer on the Reviews team at Tom’s Guide where she tests out the latest tech so you can know what’s going to be worth your time and money. She has a master’s degree in Magazine Journalism and a bachelor’s degree in Journalism, Media and Sociology from Cardiff University. She has bylines in titles including Women’s Health UK, sharing the latest health and wellness news stories, and Virgin Radio UK, providing the latest entertainment news and working on celebrity interviews. She has experience reporting on a variety of topics including music, literature, motorsport, entertainment and health. In previously published work, she has reviewed live music events, books, and wellness products. She values the importance of tech enhancing your life rather than taking over, and as a music fanatic, she is always looking for the best way to listen to new music releases. Discovering the top audio equipment that enhances sound quality and provides optimum comfort is what Ashley does best. In her spare time, Ashley enjoys hitting her reading goals on Goodreads by getting into the latest novels with a cup of tea as well as getting out in the Welsh mountains for a good hike on the weekend.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/boLRuFNtDaDzwRwUeNtDAW-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Dualit Design Series Kettle photographed in front of a blue background]]></media:description>                                                            <media:text><![CDATA[Dualit Design Series Kettle photographed in front of a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[Dualit Design Series Kettle photographed in front of a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/boLRuFNtDaDzwRwUeNtDAW-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As a tea-drinking Brit, I have big opinions on kettles, and the Dualit Design Series Kettle is a good option for most people’s homes. It has an aesthetic stainless steel design which is easy to maintain with regular cleaning. It has good heat retention and is easy and accurate to pour with. </p><p>Unfortunately, though, the window design makes it hard to read the measurements when filling the kettle. The boiling is also a little slow when compared to other kettles. </p><p>To find out more about the kitchen appliance, you can keep reading this full Dualit Design Series Kettle review. </p><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-specs"><span>Dualit Design Series Kettle review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Dualit-72955-Design-Kettle-Black/dp/B00UHKSRMG" target="_blank" rel="nofollow">$178</a></p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>1.5L</p></td></tr><tr><td class="firstcol " ><p><strong>Rated power</strong></p></td><td  ><p>1.5kW</p></td></tr><tr><td class="firstcol " ><p><strong>Exterior material</strong></p></td><td  ><p>Stainless steel</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>9.4 x 8.6 x 5.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>3.2 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Features</strong></p></td><td  ><p>Interchangeable panels, Boil-dry protection, Pure pour spout</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-price-availability"><span>Dualit Design Series Kettle review: Price & availability</span></h2><p>The Dualit Design Series Kettle is available to purchase from <a href="https://www.amazon.com/Dualit-72955-Design-Kettle-Black/dp/B00UHKSRMG" target="_blank" rel="nofollow">Amazon U.S. for $178</a> or directly from the <a href="https://us.dualit.com/products/design-series-kettle" target="_blank" rel="nofollow">Dualit online store for $179</a>. You can also purchase the kettle in the U.K., where it’s called the <a href="https://www.tomsguide.com/reviews/dualit-architect-kettle">Dualit Architect Kettle</a>. </p><p>The kettle has a stainless steel finish, but you can purchase <a href="https://www.dualit.com/products/architect-kettle-panels" target="_blank" rel="nofollow">interchangeable panels </a>for the bottom section from the Dualit store to make it your own. These include the colors Shadow, Metallic Silver, Copper, Brass, Evergreen and Eucalyptus. </p><p>Dualit also sells the <a href="https://www.tomsguide.com/home/home-appliances/dualit-classic-kettle-review">Classic Kettle</a> for $249, which is more of a fashion statement than a standard kettle. Its curved shape is much more interesting than the Design Series — the irony is not lost on me.</p><p>But you don’t need to spend the best part of $200 for a good kettle. I have also tested the <a href="https://www.tomsguide.com/home/home-appliances/dualit-classic-kettle-review">Cosori Electric Glass Kettle</a>, which, while not as dashing, is a fraction of the price at just $27. And honestly, it does just as good a job as the Design Series Kettle — you just have to be willing to put up with the odd leak while pouring, which isn’t a worry with the Dualit.</p><p>While the Dualit has a Pure pour spout to ensure zero leaks when pouring, if you need even more precision for drinks such as pour-over coffee, you may want to consider a gooseneck kettle such as the <a href="https://www.tomsguide.com/home/home-appliances/fellow-stagg-ekg-kettle-review">Fellow Stagg EKG</a> for $165. If you take your brews seriously, I’d recommend the Fellow over the Dualit.</p><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-design-features"><span>Dualit Design Series Kettle review: Design & features</span></h2><p>The Dualit Design Series Kettle is certainly eye-catching. It measures 9.4 x 8.6 x 5.5 inches, weighs 3.2 pounds, and has a reflective surface that will have you checking out your hair while the water boils. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vQQ7jxTzikipJHUeGEbBHW" name="Dualit Design Series Kettle" alt="Dualit Design Series Kettle photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/vQQ7jxTzikipJHUeGEbBHW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>It can boil a maximum capacity of 1.5 liters, enough for six cups. However, you’ll find yourself playing a guessing game when filling it because of the design and positioning of the measurement window. The window has a fish-eye effect, warping the water as you peer through, and it sits directly in line with the handle, so you need to look around that too. </p><p>The handle extends down most of the body, making it easy to hold. At the top of the handle there’s a button to release the flat, hinged lid. This means you can open it without touching it, but to close it you’ll need to push it down. If you’ve already boiled the kettle once, be wary as this part can get very hot. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ugUvCUdXy2Q2SLczdobUiW" name="Dualit Design Series Kettle" alt="Dualit Design Series Kettle photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/ugUvCUdXy2Q2SLczdobUiW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The power switch is just below the handle at the base. This base is very cohesive with the rest of the kettle — it looks like one whole unit with the kettle on top. It has a three-foot power cord, so it is best set up near a power outlet. If you need to make this shorter, you can store the excess in the base.</p><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-performance"><span>Dualit Design Series Kettle review: Performance </span></h2><p>I have to say, when testing the Dualit Design Series Kettle, I was a little confused. After testing the Dualit Classic Kettle, I was impressed at how fast it boiled the water. It boiled five cups in just two minutes and 35 seconds. The Design Series Kettle took three minutes and 11 seconds — a stark difference. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3TE8WQrPykucFMwxWgcVwW" name="Dualit Design Series Kettle" alt="Dualit Design Series Kettle photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/3TE8WQrPykucFMwxWgcVwW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>All of my testing conditions were the same, so if you want an appliance that won’t have you waiting around all day, spring for the more expensive Classic. You may be spending a little extra, but hey, you’ll get to enjoy life with your fresh brew instead of staring into your lifeless eyes in the kettle’s reflection as the water boils for an extra minute. Just me? Well, as a tea-drinking Brit, by the time I’ve made my daily quota, all of those minutes add up.</p><div ><table><tbody><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p><strong>Boiling time</strong></p></td></tr><tr><td class="firstcol " ><p><strong>2 cups</strong></p></td><td  ><p>1 minute, 35 seconds</p></td></tr><tr><td class="firstcol " ><p><strong>3 cups</strong></p></td><td  ><p>2 minutes, 5 seconds</p></td></tr><tr><td class="firstcol " ><p><strong>4 cups </strong></p></td><td  ><p>2 minutes, 41 seconds</p></td></tr><tr><td class="firstcol " ><p><strong>5 cups </strong></p></td><td  ><p>3 minutes, 11 seconds</p></td></tr><tr><td class="firstcol " ><p><strong>Max</strong></p></td><td  ><p>3 minutes, 53 seconds</p></td></tr></tbody></table></div><p>I measured the temperature of the water immediately after boiling and got a reading of 209.3ºF/98.5ºC. This was very slightly cooler than the 210ºF/99.3ºC reading I got from the Classic Kettle. But the Design Series holds heat pretty well. After an hour, I got a reading of 161.1ºF/71.7ºC. So, if you wanted to reboil the water, you won’t be waiting for the full *checks notes* three minutes and 53 seconds — yikes. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ipma4RqBU5Rb5HruMitjYW" name="Dualit Design Series Kettle" alt="Dualit Design Series Kettle photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/ipma4RqBU5Rb5HruMitjYW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Pouring with the kettle is a dream. If you’re a little shaky when pouring, I would fully recommend this kettle. I encountered no spillages. And the water went exactly where I needed it to. It can be a little heavy when you fill it to the top, so I only ever filled it about halfway so I could still use it with ease. </p><p>The kettle isn’t too loud either, and gave me a reading of 76dB on the Decibel X app.</p><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-storage-maintenance"><span>Dualit Design Series Kettle review: Storage & maintenance </span></h2><p>The Dualit Design Series is a kettle, and as such, this is a generally straightforward affair. It’s easy to maintain; you’ll just find yourself wiping down the exterior more than your usual kettle given its reflective nature. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iyrLxfLyPwcSnTSiRmWXuW" name="Dualit Design Series Kettle" alt="Dualit Design Series Kettle photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/iyrLxfLyPwcSnTSiRmWXuW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you live in a hard water area, you’ll also need to descale the kettle regularly. You can purchase descaling tablets for this or use natural <a href="https://www.tomsguide.com/home/how-to-clean-a-kettle-7-simple-steps">methods such as lemon juice or baking soda</a>. The spout of the kettle also has a removable limescale filter which is easy to remove, clean, and refit. </p><h2 class="article-body__section" id="section-dualit-design-series-kettle-review-verdict"><span>Dualit Design Series Kettle review: Verdict</span></h2><p>The Dualit Design Series Kettle is undeniably good-looking. The stainless steel finish is slick and will fit in with any kitchen. The kettle is also easy to maintain (if you don’t mind wiping it down a lot). It has good heat retention and is very easy to pour with. There were absolutely zero drips thanks to the Pure pour spout. </p><p>Unfortunately, it’s tricky to tell where the water has been filled up to because of the poor window design, and boiling is a little slow. It took a whole minute longer to boil the same volume of water as the Dualit Classic Kettle.</p><p>If sleek looks are your priority, this is a great kettle, but $178 is a lot of money to spend on a kettle that takes so long to boil. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested one of the coolest-looking drip coffee makers in the world — and it's not just a pretty face ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Smeg Retro Coffee Maker is certainly gorgeous enough to be on <em>the</em> catwalk model of coffee machines. As with seemingly everything else made by the Italian coffee brand, the Retro (DCF02) looks sleek, sophisticated, and effortlessly cool. </p><p>But is appearance enough to make it one of the <a href="https://www.tomsguide.com/best-picks/best-coffee-makers">best coffee maker</a>s? Thankfully, this time, the Smeg Retro Coffee Maker isn’t style over substance. I wasn’t a huge fan of the <a href="https://www.tomsguide.com/home/home-appliances/smeg-espresso-machine-review">Smeg ECF02’s </a>performance despite a premium appearance — but the Retro Coffee Maker can walk the walk <em>and</em> talk the talk. It brews clean-tasting, balanced coffee with minimal silt and zero bitterness. And all you have to do is press one button. </p><p>So, want to find out more about this gorgeous coffee machine? Spoiler alert: it’s an absolute treat. To get the full story, keep reading this Smeg Retro Coffee Maker review. </p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-specs"><span>Smeg Retro Coffee Maker: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.crateandbarrel.com/smeg-matte-mocha-drip-coffee-maker/s644835" target="_blank" rel="nofollow">$259</a> / <a href="https://www.johnlewis.com/smeg-dcf02-drip-filter-coffee-machine/navy-blue/p5176025" target="_blank" rel="nofollow">£199-£219</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>7.5 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>10 x 10 x 14 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Filters</strong></p></td><td  ><p>Permanent / #4</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>Filter, scoop</p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>47.25 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Colors/Materials</strong></p></td><td  ><p>Moonlight, cream, navy, black, pastel blue, pastel green, silver, gray, red, white / Stainless steel, plastic, chrome, glass</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-price-availability"><span>Smeg Retro Coffee Maker review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="iqDpK6KWDxuTaxrm2BmEM6" name="Smeg_Drip_4" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/iqDpK6KWDxuTaxrm2BmEM6.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Smeg Retro Coffee Maker is <a href="https://www.crateandbarrel.com/smeg-matte-mocha-drip-coffee-maker/s644835" target="_blank" rel="nofollow">$259 from Crate&Barrel in the U.S.</a>, regardless of color, and <a href="https://www.johnlewis.com/smeg-dcf02-drip-filter-coffee-machine/navy-blue/p5176025" target="_blank" rel="nofollow">£199-£219 from John Lewis in the U.K</a><a href="https://www.johnlewis.com/smeg-dcf02-drip-filter-coffee-machine/navy-blue/p5176025" target="_blank">.</a> The Moonlight colorway, the model I tested, runs for £20 more than the standard colors: cream, navy, black, pastel blue, pastel green, silver, gray, red, and white. </p><p>In the U.S., though, Moonlight is the usual price of $259. Functionally, the “standard” colors and Moonlight are the same, so if you’re looking to save as much as possible, I’d go for the older colors. But isn’t the Moonlight oh so pretty? Just look at it!</p><p>In keeping with Smeg’s back catalogue, the Retro Coffee Maker is pretty pricey for what it is. This is a common theme with Smeg, though: you pay a premium for the style. </p><p>For $369, you could get the <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster KGBV Select</a>, which has a 5-year warranty and lifetime repairs. For $250, you could get the single-serve <a href="https://www.tomsguide.com/home/home-appliances/moccamaster-cup-one-review">Moccamaster Cup-One</a>, complete with the same after-purchase benefits.</p><p>I’ve also tested the SCA-accredited $329 <a href="https://www.tomsguide.com/home/coffee-makers/breville-luxe-brewer-review">Breville Luxe Brewer</a> and the low-plastic $319 Aarke Coffee Maker, alongside the low-plastic Ratio Four ($259), an automated pour-over. Although it’s not available in the U.K., so I haven’t tested it, I can assure you that the budget $90 Ninja 12-Cup Drip Brewer is a crowd pleaser in Tom’s Guide’s New York office. </p><p>Bonavita and Braun have also mastered the budget drip coffee maker: <a href="https://www.amazon.com/Bonavita-Connoisseur-One-Touch-Featuring-BV1901TS/dp/B076PFMRGX" target="_blank" rel="nofollow">Bonavita’s SCA-accredited 8-Cup Brewer</a> is just $189, the<a href="https://www.amazon.com/Bonavita-One-Touch-Featuring-Thermal-BV1500TS/dp/B00SK5IXPQ" target="_blank" rel="nofollow"> 5-Cup is even cheaper, at $149</a>. The <a href="https://www.amazon.com/Braun-KF7150BK-Sense-Coffee-Maker/dp/B01EA5ZHIA" target="_blank" rel="nofollow">12-Cup Braun Drip Coffee Maker</a> is $129. This is where I’d like to see the Smeg Retro Coffee Maker’s price due to its lack of SCA accreditation and mostly plastic build. </p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-design"><span>Smeg Retro Coffee Maker review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="oHksEmdhmWNcHy4uq9DUW6" name="Smeg_Drip_10" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/oHksEmdhmWNcHy4uq9DUW6.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As with everything else under Smeg’s belt, the Retro Coffee Maker looks really cool. Per the name, it’s certainly retro, and would look at home in either a diner on a cheesy high school drama (I’m thinking Riverdale, anyone?), or in any sleek, sophisticated home kitchen. </p><p>I tested the gorgeous Moonlight shade, which has the same muted gray buttons as the rest of the color line. The buttons control power, brew amount, auto start, and flavor intensity. The lever is solely for adjusting the machine’s time display and the auto start timer. At first, I thought this was an unnecessary use of space, but it’s actually really fun to use. </p><p>It’s worth noting that the Retro Coffee Maker is bigger than it looks online: it’s 14 inches tall, 10 inches wide, and 10 inches deep. If you’ve got a small kitchen, I’d measure your space before purchasing. Comparatively, the Moccamaster KGBV Select is 6.5 x 12.8 x 14 inches, the Breville Luxe Brewer is 14.7 x 7 x 16.1 inches, and the Ninja 12-Cup is 8 x 10 x 14 inches. The Moccamaster is the narrowest, but it’s longer and the same height as the Smeg. </p><p>As for accessories, the Retro Coffee Maker comes with just two: a scoop and a permanent mesh filter. You can use single-use #4 conical filters, but I tested with the reusable filter as provided.</p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-performance"><span>Smeg Retro Coffee Maker review: Performance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="XzLkjE5GhYvseJrd5rQfC6" name="Smeg_Drip_5" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/XzLkjE5GhYvseJrd5rQfC6.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Smeg Retro Coffee Maker is supremely easy to use. All you have to do is put your coffee in the filter, add your water, and press go. </p><p>Smeg recommends a recipe of one scoop per cup, so if you’re making a full batch, that would be 10 scoops. A half-batch requires 4 scoops. Of course, you can adjust this recipe to your tastes. </p><p>A full batch sometimes took seven minutes, sometimes eight, and a half batch took around three minutes. This is a little slower than the Moccamaster KGBV Select, but not by much at all. </p><p>The water tank isn’t removable, so I poured water in using the carafe. I have two qualms with this: one, the carafe is angled in such a way that it’s tricky to get the final dregs of water, and two, the water tank opening is on the right side, so if you’re left-handed like me, this will pose an issue. Alas, this will only irritate 11% of the population, so it’s not enough to knock half a star off (but justice for us devil-worshippers [for legal reasons, this is a joke]). </p><p>Here’s how I made delicious coffee on the Smeg Retro Coffee Maker. </p><h2 id="my-go-to-smeg-retro-coffee-maker-routine">My go-to Smeg Retro Coffee Maker routine </h2><p>Smeg recommends one scoop per cup. One scoop was between 8.5-10 grams depending on if I levelled it or not. </p><p>I used 87g of coffee (10 level scoops) for a full carafe, which works out to a ratio of 1:16.</p><ol start="1"><li>Fill the water tank to max.</li><li>Grind your coffee to a medium size. I used my Mazzer Philos, the best grinder in the world.</li><li>Put your desired amount of coffee in the filter. I used 87g for a full batch, a 1:16 ratio.</li><li>Select your desired intensity level. I used level 1.</li><li>Press the power button to start your brew.</li><li>Wait 7-8 minutes!</li></ol><p>And that’s it! </p><p>Here’s a photo of the grounds after brewing. As you can see, there’s very little funnelling here.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4345px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="yFZVXxTCR72NySqvbNjdsa" name="smeg coffee maker funnelling" alt="a photo of the smeg retro coffee maker after brewing showing coffee grounds" src="https://cdn.mos.cms.futurecdn.net/yFZVXxTCR72NySqvbNjdsa.png" mos="" align="middle" fullscreen="" width="4345" height="2444" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>The Retro Coffee Maker brews clean, fresh-tasting coffee. You can obviously adjust the intensity using either the on-machine button or increasing the coffee dosage (or grinding finer), but the out-of-the-box flavor is great. </p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-storage-maintenance"><span>Smeg Retro Coffee Maker review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="y7hVofnDRNZuY72Btk6aP6" name="Smeg_Drip_8" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/y7hVofnDRNZuY72Btk6aP6.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I was pleasantly surprised to see the Smeg Retro Coffee Maker come with a 2-year warranty. This is better than Ninja’s 1-year warranty, but still not comparable to Moccamaster’s 5-year warranty (and lifetime repairs) or Simply Good Coffee’s new 5-year standard warranty. </p><p>To keep your Retro Coffee Maker lasting as long as possible, do not put the carafe in the dishwasher. I put the mesh filter in the dishwasher and it survived, but I’d recommend only washing by hand. </p><p>As mentioned earlier, the Retro Coffee Maker is pretty large — so make sure you’ve got enough counter space before buying!</p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-how-does-it-compare"><span>Smeg Retro Coffee Maker review: How does it compare? </span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="j9BEpPLzE92N3QSVchziJ6" name="Smeg_Drip_2" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/j9BEpPLzE92N3QSVchziJ6.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Smeg Retro Coffee Maker looks just as cool as the industrial-chic Moccamaster, so I’d recommend either of these brands if you’re after that vintage vibe. I’d also put Simply Good Coffee’s Plastic-Free Brewer in this category — it looks sleek and effortlessly cool. </p><p>The Retro Coffee Maker isn’t as fast as the Breville Luxe Brewer, but it is $70 cheaper, so you win some, you lose some. It’s also considerably cheaper than the $319 Aarke Coffee Maker — but the Aarke is low-plastic, which is what a lot of consumers want these days. </p><p><em>But</em>, and that’s a relatively big but, I would say the Smeg Retro Coffee Maker is a touch overpriced for what it is. The Moccamaster is $369 because it has lifetime repairs and is SCA-accredited. The SGC Plastic-Free Brewer is $469 because it’s made almost entirely from glass and metal. The Breville Luxe Brewer is $329 because it brews in three minutes. </p><p>As mentioned earlier, I’d like to see Smeg’s pricing fall more in line with Ninja’s, Bonavita’s, and Braun (or get SCA accreditation or improve the materials/after-purchase care). Ninja’s 12-Cup Drip Brewer is just $89, and Bonavita’s SCA-accredited 8-Cup Brewer is just $189. The 5-Cup is even cheaper — $149. The 12-Cup Braun Drip Coffee Maker is $129. </p><p>The Smeg doesn’t really have any USPs other than looking really cool. While I do objectively like this coffee maker, I’ll be the first person to admit it falls into Smeg’s main pitfall: it’s expensive for what it is.</p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-verdict"><span> Smeg Retro Coffee Maker review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="HKZn7AcLGQso6zcD35TJY6" name="Smeg_Drip_9" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/HKZn7AcLGQso6zcD35TJY6.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Despite being a little pricey for what it is, the Smeg Retro Coffee Maker gets a big thumbs up from me. I thoroughly enjoyed testing this machine and would recommend it for people who value style and ease of use.</p><p>Yes, it’s expensive considering its plastic build and lack of SCA accreditation, and no, it doesn’t have any unique features like a lifetime repairs warranty or an SCA accreditation. The auto-start timer is cool, but the $89 Ninja 12-Cup has this, too. However, just look at it. Doesn’t that speak for itself? </p><p>So, if you want a kitchen centrepiece that’ll have guests going “Ooh” and “Aah” and “Where did you get that from?” then you’ll get on just fine with the Smeg Retro Coffee Maker. It’s literally plug-and-play, and tastes as good as it looks.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/smeg-retro-coffee-maker-review</link>
                                                                            <description>
                            <![CDATA[ The Smeg Retro Coffee Maker brews clean, delicious coffee with a single press of a button, and looks delightfully cool while doing so, too. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">S8FUcK3E8VNEZckVuHdGLd</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/SvTaZ7daV4Lbis8RvtwDU6-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 07 Aug 2026 14:58:19 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/SvTaZ7daV4Lbis8RvtwDU6-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the smeg retro coffee maker photographed against the blue tom&#039;s guide background]]></media:description>                                                            <media:text><![CDATA[the smeg retro coffee maker photographed against the blue tom&#039;s guide background]]></media:text>
                                <media:title type="plain"><![CDATA[the smeg retro coffee maker photographed against the blue tom&#039;s guide background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/SvTaZ7daV4Lbis8RvtwDU6-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Smeg Retro Coffee Maker is certainly gorgeous enough to be on <em>the</em> catwalk model of coffee machines. As with seemingly everything else made by the Italian coffee brand, the Retro (DCF02) looks sleek, sophisticated, and effortlessly cool. </p><p>But is appearance enough to make it one of the <a href="https://www.tomsguide.com/best-picks/best-coffee-makers">best coffee maker</a>s? Thankfully, this time, the Smeg Retro Coffee Maker isn’t style over substance. I wasn’t a huge fan of the <a href="https://www.tomsguide.com/home/home-appliances/smeg-espresso-machine-review">Smeg ECF02’s </a>performance despite a premium appearance — but the Retro Coffee Maker can walk the walk <em>and</em> talk the talk. It brews clean-tasting, balanced coffee with minimal silt and zero bitterness. And all you have to do is press one button. </p><p>So, want to find out more about this gorgeous coffee machine? Spoiler alert: it’s an absolute treat. To get the full story, keep reading this Smeg Retro Coffee Maker review. </p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-specs"><span>Smeg Retro Coffee Maker: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.crateandbarrel.com/smeg-matte-mocha-drip-coffee-maker/s644835" target="_blank" rel="nofollow">$259</a> / <a href="https://www.johnlewis.com/smeg-dcf02-drip-filter-coffee-machine/navy-blue/p5176025" target="_blank" rel="nofollow">£199-£219</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>7.5 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>10 x 10 x 14 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Filters</strong></p></td><td  ><p>Permanent / #4</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>Filter, scoop</p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>47.25 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Colors/Materials</strong></p></td><td  ><p>Moonlight, cream, navy, black, pastel blue, pastel green, silver, gray, red, white / Stainless steel, plastic, chrome, glass</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-price-availability"><span>Smeg Retro Coffee Maker review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="iqDpK6KWDxuTaxrm2BmEM6" name="Smeg_Drip_4" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/iqDpK6KWDxuTaxrm2BmEM6.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Smeg Retro Coffee Maker is <a href="https://www.crateandbarrel.com/smeg-matte-mocha-drip-coffee-maker/s644835" target="_blank" rel="nofollow">$259 from Crate&Barrel in the U.S.</a>, regardless of color, and <a href="https://www.johnlewis.com/smeg-dcf02-drip-filter-coffee-machine/navy-blue/p5176025" target="_blank" rel="nofollow">£199-£219 from John Lewis in the U.K</a><a href="https://www.johnlewis.com/smeg-dcf02-drip-filter-coffee-machine/navy-blue/p5176025" target="_blank">.</a> The Moonlight colorway, the model I tested, runs for £20 more than the standard colors: cream, navy, black, pastel blue, pastel green, silver, gray, red, and white. </p><p>In the U.S., though, Moonlight is the usual price of $259. Functionally, the “standard” colors and Moonlight are the same, so if you’re looking to save as much as possible, I’d go for the older colors. But isn’t the Moonlight oh so pretty? Just look at it!</p><p>In keeping with Smeg’s back catalogue, the Retro Coffee Maker is pretty pricey for what it is. This is a common theme with Smeg, though: you pay a premium for the style. </p><p>For $369, you could get the <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster KGBV Select</a>, which has a 5-year warranty and lifetime repairs. For $250, you could get the single-serve <a href="https://www.tomsguide.com/home/home-appliances/moccamaster-cup-one-review">Moccamaster Cup-One</a>, complete with the same after-purchase benefits.</p><p>I’ve also tested the SCA-accredited $329 <a href="https://www.tomsguide.com/home/coffee-makers/breville-luxe-brewer-review">Breville Luxe Brewer</a> and the low-plastic $319 Aarke Coffee Maker, alongside the low-plastic Ratio Four ($259), an automated pour-over. Although it’s not available in the U.K., so I haven’t tested it, I can assure you that the budget $90 Ninja 12-Cup Drip Brewer is a crowd pleaser in Tom’s Guide’s New York office. </p><p>Bonavita and Braun have also mastered the budget drip coffee maker: <a href="https://www.amazon.com/Bonavita-Connoisseur-One-Touch-Featuring-BV1901TS/dp/B076PFMRGX" target="_blank" rel="nofollow">Bonavita’s SCA-accredited 8-Cup Brewer</a> is just $189, the<a href="https://www.amazon.com/Bonavita-One-Touch-Featuring-Thermal-BV1500TS/dp/B00SK5IXPQ" target="_blank" rel="nofollow"> 5-Cup is even cheaper, at $149</a>. The <a href="https://www.amazon.com/Braun-KF7150BK-Sense-Coffee-Maker/dp/B01EA5ZHIA" target="_blank" rel="nofollow">12-Cup Braun Drip Coffee Maker</a> is $129. This is where I’d like to see the Smeg Retro Coffee Maker’s price due to its lack of SCA accreditation and mostly plastic build. </p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-design"><span>Smeg Retro Coffee Maker review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="oHksEmdhmWNcHy4uq9DUW6" name="Smeg_Drip_10" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/oHksEmdhmWNcHy4uq9DUW6.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As with everything else under Smeg’s belt, the Retro Coffee Maker looks really cool. Per the name, it’s certainly retro, and would look at home in either a diner on a cheesy high school drama (I’m thinking Riverdale, anyone?), or in any sleek, sophisticated home kitchen. </p><p>I tested the gorgeous Moonlight shade, which has the same muted gray buttons as the rest of the color line. The buttons control power, brew amount, auto start, and flavor intensity. The lever is solely for adjusting the machine’s time display and the auto start timer. At first, I thought this was an unnecessary use of space, but it’s actually really fun to use. </p><p>It’s worth noting that the Retro Coffee Maker is bigger than it looks online: it’s 14 inches tall, 10 inches wide, and 10 inches deep. If you’ve got a small kitchen, I’d measure your space before purchasing. Comparatively, the Moccamaster KGBV Select is 6.5 x 12.8 x 14 inches, the Breville Luxe Brewer is 14.7 x 7 x 16.1 inches, and the Ninja 12-Cup is 8 x 10 x 14 inches. The Moccamaster is the narrowest, but it’s longer and the same height as the Smeg. </p><p>As for accessories, the Retro Coffee Maker comes with just two: a scoop and a permanent mesh filter. You can use single-use #4 conical filters, but I tested with the reusable filter as provided.</p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-performance"><span>Smeg Retro Coffee Maker review: Performance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="XzLkjE5GhYvseJrd5rQfC6" name="Smeg_Drip_5" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/XzLkjE5GhYvseJrd5rQfC6.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Smeg Retro Coffee Maker is supremely easy to use. All you have to do is put your coffee in the filter, add your water, and press go. </p><p>Smeg recommends a recipe of one scoop per cup, so if you’re making a full batch, that would be 10 scoops. A half-batch requires 4 scoops. Of course, you can adjust this recipe to your tastes. </p><p>A full batch sometimes took seven minutes, sometimes eight, and a half batch took around three minutes. This is a little slower than the Moccamaster KGBV Select, but not by much at all. </p><p>The water tank isn’t removable, so I poured water in using the carafe. I have two qualms with this: one, the carafe is angled in such a way that it’s tricky to get the final dregs of water, and two, the water tank opening is on the right side, so if you’re left-handed like me, this will pose an issue. Alas, this will only irritate 11% of the population, so it’s not enough to knock half a star off (but justice for us devil-worshippers [for legal reasons, this is a joke]). </p><p>Here’s how I made delicious coffee on the Smeg Retro Coffee Maker. </p><h2 id="my-go-to-smeg-retro-coffee-maker-routine">My go-to Smeg Retro Coffee Maker routine </h2><p>Smeg recommends one scoop per cup. One scoop was between 8.5-10 grams depending on if I levelled it or not. </p><p>I used 87g of coffee (10 level scoops) for a full carafe, which works out to a ratio of 1:16.</p><ol start="1"><li>Fill the water tank to max.</li><li>Grind your coffee to a medium size. I used my Mazzer Philos, the best grinder in the world.</li><li>Put your desired amount of coffee in the filter. I used 87g for a full batch, a 1:16 ratio.</li><li>Select your desired intensity level. I used level 1.</li><li>Press the power button to start your brew.</li><li>Wait 7-8 minutes!</li></ol><p>And that’s it! </p><p>Here’s a photo of the grounds after brewing. As you can see, there’s very little funnelling here.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4345px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="yFZVXxTCR72NySqvbNjdsa" name="smeg coffee maker funnelling" alt="a photo of the smeg retro coffee maker after brewing showing coffee grounds" src="https://cdn.mos.cms.futurecdn.net/yFZVXxTCR72NySqvbNjdsa.png" mos="" align="middle" fullscreen="" width="4345" height="2444" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>The Retro Coffee Maker brews clean, fresh-tasting coffee. You can obviously adjust the intensity using either the on-machine button or increasing the coffee dosage (or grinding finer), but the out-of-the-box flavor is great. </p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-storage-maintenance"><span>Smeg Retro Coffee Maker review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="y7hVofnDRNZuY72Btk6aP6" name="Smeg_Drip_8" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/y7hVofnDRNZuY72Btk6aP6.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I was pleasantly surprised to see the Smeg Retro Coffee Maker come with a 2-year warranty. This is better than Ninja’s 1-year warranty, but still not comparable to Moccamaster’s 5-year warranty (and lifetime repairs) or Simply Good Coffee’s new 5-year standard warranty. </p><p>To keep your Retro Coffee Maker lasting as long as possible, do not put the carafe in the dishwasher. I put the mesh filter in the dishwasher and it survived, but I’d recommend only washing by hand. </p><p>As mentioned earlier, the Retro Coffee Maker is pretty large — so make sure you’ve got enough counter space before buying!</p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-how-does-it-compare"><span>Smeg Retro Coffee Maker review: How does it compare? </span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="j9BEpPLzE92N3QSVchziJ6" name="Smeg_Drip_2" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/j9BEpPLzE92N3QSVchziJ6.jpg" mos="" align="middle" fullscreen="" width="1920" height="1079" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Smeg Retro Coffee Maker looks just as cool as the industrial-chic Moccamaster, so I’d recommend either of these brands if you’re after that vintage vibe. I’d also put Simply Good Coffee’s Plastic-Free Brewer in this category — it looks sleek and effortlessly cool. </p><p>The Retro Coffee Maker isn’t as fast as the Breville Luxe Brewer, but it is $70 cheaper, so you win some, you lose some. It’s also considerably cheaper than the $319 Aarke Coffee Maker — but the Aarke is low-plastic, which is what a lot of consumers want these days. </p><p><em>But</em>, and that’s a relatively big but, I would say the Smeg Retro Coffee Maker is a touch overpriced for what it is. The Moccamaster is $369 because it has lifetime repairs and is SCA-accredited. The SGC Plastic-Free Brewer is $469 because it’s made almost entirely from glass and metal. The Breville Luxe Brewer is $329 because it brews in three minutes. </p><p>As mentioned earlier, I’d like to see Smeg’s pricing fall more in line with Ninja’s, Bonavita’s, and Braun (or get SCA accreditation or improve the materials/after-purchase care). Ninja’s 12-Cup Drip Brewer is just $89, and Bonavita’s SCA-accredited 8-Cup Brewer is just $189. The 5-Cup is even cheaper — $149. The 12-Cup Braun Drip Coffee Maker is $129. </p><p>The Smeg doesn’t really have any USPs other than looking really cool. While I do objectively like this coffee maker, I’ll be the first person to admit it falls into Smeg’s main pitfall: it’s expensive for what it is.</p><h2 class="article-body__section" id="section-smeg-retro-coffee-maker-review-verdict"><span> Smeg Retro Coffee Maker review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1919px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="HKZn7AcLGQso6zcD35TJY6" name="Smeg_Drip_9" alt="the smeg retro coffee maker photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/HKZn7AcLGQso6zcD35TJY6.jpg" mos="" align="middle" fullscreen="" width="1919" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Despite being a little pricey for what it is, the Smeg Retro Coffee Maker gets a big thumbs up from me. I thoroughly enjoyed testing this machine and would recommend it for people who value style and ease of use.</p><p>Yes, it’s expensive considering its plastic build and lack of SCA accreditation, and no, it doesn’t have any unique features like a lifetime repairs warranty or an SCA accreditation. The auto-start timer is cool, but the $89 Ninja 12-Cup has this, too. However, just look at it. Doesn’t that speak for itself? </p><p>So, if you want a kitchen centrepiece that’ll have guests going “Ooh” and “Aah” and “Where did you get that from?” then you’ll get on just fine with the Smeg Retro Coffee Maker. It’s literally plug-and-play, and tastes as good as it looks.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Overwhelmed by coffee jargon? Here are the 5 key espresso terms you need to know ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the nineteenth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>Getting into coffee can feel impossible at first. But fret not! I'm here to help. I'm an ex-barista and now a professional coffee tester, so I can help you understand the most important parts of your <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">espresso machine</a>. </p><p>If you're a total newbie, forget everything you've learned so far. These five key terms are all you need to know right now. Of course, the more your confidence grows, the more you'll absorb and retain, but if you're pulling out your hair and stressing over niche jargon on the daily, you're doing yourself a disservice. You don't need to be perfect on the first try! All you need is an espresso machine and a clear head. I can take care of the rest. </p><p>So strap in as I teach you exactly what you need to know to start pulling shots like a professional barista. And, most importantly, have fun! Coffee is all about enjoyment. If you're doing things "wrong" but having fun, go you! That's all that matters.</p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7672401168542240031" data-video-id="7672401168542240031" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ miffy cafe - sakuracloud" href="https://www.tiktok.com/music/miffy-cafe-7425305480727889936">♬ miffy cafe - sakuracloud</a></section>                    </blockquote></div>                <h2 id="what-s-a-portafilter">What's a portafilter?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4005px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3q8RoMEyez5a5LZgLy6wZQ" name="complete portafilter" alt="an assembled portafilter" src="https://cdn.mos.cms.futurecdn.net/3q8RoMEyez5a5LZgLy6wZQ.jpg" mos="" align="middle" fullscreen="" width="4005" height="2253" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>A portafilter is the handle of the basket. I've also seen this called a group handle. </p><p>A portafilter basket is where you put your coffee. This is where the espresso machine will send water to extract your espresso.</p><p>That's it! I told you it was easy. </p><h2 id="what-s-a-tamper">What's a tamper?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8YvyWPttRVe2oK99sgwBFa" name="Sage_Breville_Bambino_Plus_ 3.JPG" alt="the breville bambino plus in stainless steel silver photographed against a blue background shown with its accessories including milk jug, tamper, portafilter, and shown in use extracting espresso" src="https://cdn.mos.cms.futurecdn.net/8YvyWPttRVe2oK99sgwBFa.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The tamper is the circular accessory you use to tamp (press down on) the coffee grounds. If you hear someone say tamp the coffee, this is what they're referring to. You want to apply around 20kg of pressure — which basically means just push hard, but be mindful to keep the coffee level. </p><p>This is to flatten and compact the coffee grounds and create an even surface for the water to flow through (preventing channeling).</p><h2 id="what-s-channeling">What's channeling? </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="geSoGaVosh2iqmkZZqJTzZ" name="Sage_bambino_ 11.JPG" alt="the breville bambino / sage bambino in silver photographed against a blue background with a wooden feature wall, showing the steam wand, group head, water tank, button controls, and espresso" src="https://cdn.mos.cms.futurecdn.net/geSoGaVosh2iqmkZZqJTzZ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Channeling is where water flows through <em>channels</em> in the coffee rather than saturating the entire portafilter. This is caused by an uneven grind, uneven tamp, or an uneven dose. When you put coffee in the portafilter basket, you want to make sure it's flat, evenly distributed, and there are no major clumps. </p><p>A channeled espresso will taste sour and bitter and generally unpleasant. Nothing you wouldn't be able to mask with syrups, though!</p><h2 id="what-s-puck-prep">What's puck prep? </h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/5i3fsSEGiFp3HBkwbH5DTQ.jpg" alt="The Gaggia Classic E24 Evo Pro portafilter with coffee grounds in it" /><figcaption>Before puck prep<small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ACcps3Gws3ZGy9FkVb8MiP.jpg" alt="The Gaggia Classic E24 Evo Pro portafilter with coffee grounds tamped inside it" /><figcaption>After puck prep<small role="credit">Future</small></figcaption></figure></figure><p>Puck prep is kind of a coffee snob term. It refers to everything you do to prepare your espresso. When people say, "Make sure you do your puck prep right," they're referring to the following: </p><ul><li>Grinding your beans evenly, using one of the <a href="https://www.tomsguide.com/best-picks/best-coffee-grinder">best grinders</a></li><li>Weighing out the right amount of ground coffee, using one of the <a href="https://www.tomsguide.com/best-picks/best-coffee-scales">best coffee scales</a></li><li>Putting the coffee in your portafilter basket and making sure there are no huge clumps</li><li>Tamping with proper pressure</li></ul><h2 id="what-s-overextraction-underextraction">What's overextraction/underextraction?</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/YgGsA8oa8EVyabDHtEDWd5.png" alt="A photo of espresso made on the breville bambino plus espresso machine in a shot glass with the wacaco exagram pro scale" /><figcaption>Underextracted<small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/z6zBaBfELEYmGUx7r2isY5.png" alt="A photo of espresso made on the breville bambino plus espresso machine in a shot glass with the wacaco exagram pro scale" /><figcaption>Perfect<small role="credit">Erin Bashford</small></figcaption></figure></figure><p>Both are bad! </p><p>Overextraction is where the coffee has spent too much time with water or pressure, resulting in a burned, bitter coffee. You have probably ground your coffee too coarsely.</p><p>Underextraction is where the coffee hasn't spent enough time with water or pressure, making a sour, acidic coffee. You have probably ground your coffee too finely. </p><p>If your coffee is coming out really slowly, that's overextraction. This will taste incredibly intense and burned, like charcoal. </p><p>If your coffee is coming out quickly, that's underextraction. This will taste like lemon juice. Nasty!</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-exV2aO"></div>                            </div>                            <script src="https://kwizly.com/embed/exV2aO.js" async></script><p>Did I answer all of your jargon-related questions? Let me know in the comments if you want me to explain anything else. </p><p>I release new episodes of The Coffee Lab every Friday, so be sure to check back next week for the next coffee adventure.</p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-the-new-nespresso-summer-flavors-heres-my-favorite"><strong>I tried the new Nespresso summer flavors — here's my favorite</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/im-an-ex-starbucks-barista-you-can-make-the-iced-brown-sugar-oatmilk-shaken-espresso-at-home-for-a-fraction-of-the-cost"><strong>I'm an ex-Starbucks barista — you can make the Iced Brown Sugar Oatmilk Shaken Espresso at home for a fraction of the cost</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/overwhelmed-by-coffee-jargon-here-are-the-5-key-espresso-terms-you-need-to-know</link>
                                                                            <description>
                            <![CDATA[ On Episode 19 of The Coffee Lab, I help you understand coffee equipment jargon. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">2LneZrbkc6EjjyYvC3TwWA</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/7YBnRoB7df7h9Gs3G9BjR4-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Fri, 07 Aug 2026 13:00:00 +0000</pubDate>                                                                                                                                <updated>Mon, 10 Aug 2026 14:06:58 +0000</updated>
                                                                                                                                            <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/png" url="https://cdn.mos.cms.futurecdn.net/7YBnRoB7df7h9Gs3G9BjR4-1280-80.png">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[a photo of the author drinking a coffee with a full portafilter on the other side]]></media:description>                                                            <media:text><![CDATA[a photo of the author drinking a coffee with a full portafilter on the other side]]></media:text>
                                <media:title type="plain"><![CDATA[a photo of the author drinking a coffee with a full portafilter on the other side]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/7YBnRoB7df7h9Gs3G9BjR4-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the nineteenth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>Getting into coffee can feel impossible at first. But fret not! I'm here to help. I'm an ex-barista and now a professional coffee tester, so I can help you understand the most important parts of your <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">espresso machine</a>. </p><p>If you're a total newbie, forget everything you've learned so far. These five key terms are all you need to know right now. Of course, the more your confidence grows, the more you'll absorb and retain, but if you're pulling out your hair and stressing over niche jargon on the daily, you're doing yourself a disservice. You don't need to be perfect on the first try! All you need is an espresso machine and a clear head. I can take care of the rest. </p><p>So strap in as I teach you exactly what you need to know to start pulling shots like a professional barista. And, most importantly, have fun! Coffee is all about enjoyment. If you're doing things "wrong" but having fun, go you! That's all that matters.</p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7672401168542240031" data-video-id="7672401168542240031" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ miffy cafe - sakuracloud" href="https://www.tiktok.com/music/miffy-cafe-7425305480727889936">♬ miffy cafe - sakuracloud</a></section>                    </blockquote></div>                <h2 id="what-s-a-portafilter">What's a portafilter?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4005px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3q8RoMEyez5a5LZgLy6wZQ" name="complete portafilter" alt="an assembled portafilter" src="https://cdn.mos.cms.futurecdn.net/3q8RoMEyez5a5LZgLy6wZQ.jpg" mos="" align="middle" fullscreen="" width="4005" height="2253" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>A portafilter is the handle of the basket. I've also seen this called a group handle. </p><p>A portafilter basket is where you put your coffee. This is where the espresso machine will send water to extract your espresso.</p><p>That's it! I told you it was easy. </p><h2 id="what-s-a-tamper">What's a tamper?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8YvyWPttRVe2oK99sgwBFa" name="Sage_Breville_Bambino_Plus_ 3.JPG" alt="the breville bambino plus in stainless steel silver photographed against a blue background shown with its accessories including milk jug, tamper, portafilter, and shown in use extracting espresso" src="https://cdn.mos.cms.futurecdn.net/8YvyWPttRVe2oK99sgwBFa.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The tamper is the circular accessory you use to tamp (press down on) the coffee grounds. If you hear someone say tamp the coffee, this is what they're referring to. You want to apply around 20kg of pressure — which basically means just push hard, but be mindful to keep the coffee level. </p><p>This is to flatten and compact the coffee grounds and create an even surface for the water to flow through (preventing channeling).</p><h2 id="what-s-channeling">What's channeling? </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="geSoGaVosh2iqmkZZqJTzZ" name="Sage_bambino_ 11.JPG" alt="the breville bambino / sage bambino in silver photographed against a blue background with a wooden feature wall, showing the steam wand, group head, water tank, button controls, and espresso" src="https://cdn.mos.cms.futurecdn.net/geSoGaVosh2iqmkZZqJTzZ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Channeling is where water flows through <em>channels</em> in the coffee rather than saturating the entire portafilter. This is caused by an uneven grind, uneven tamp, or an uneven dose. When you put coffee in the portafilter basket, you want to make sure it's flat, evenly distributed, and there are no major clumps. </p><p>A channeled espresso will taste sour and bitter and generally unpleasant. Nothing you wouldn't be able to mask with syrups, though!</p><h2 id="what-s-puck-prep">What's puck prep? </h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/5i3fsSEGiFp3HBkwbH5DTQ.jpg" alt="The Gaggia Classic E24 Evo Pro portafilter with coffee grounds in it" /><figcaption>Before puck prep<small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ACcps3Gws3ZGy9FkVb8MiP.jpg" alt="The Gaggia Classic E24 Evo Pro portafilter with coffee grounds tamped inside it" /><figcaption>After puck prep<small role="credit">Future</small></figcaption></figure></figure><p>Puck prep is kind of a coffee snob term. It refers to everything you do to prepare your espresso. When people say, "Make sure you do your puck prep right," they're referring to the following: </p><ul><li>Grinding your beans evenly, using one of the <a href="https://www.tomsguide.com/best-picks/best-coffee-grinder">best grinders</a></li><li>Weighing out the right amount of ground coffee, using one of the <a href="https://www.tomsguide.com/best-picks/best-coffee-scales">best coffee scales</a></li><li>Putting the coffee in your portafilter basket and making sure there are no huge clumps</li><li>Tamping with proper pressure</li></ul><h2 id="what-s-overextraction-underextraction">What's overextraction/underextraction?</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/YgGsA8oa8EVyabDHtEDWd5.png" alt="A photo of espresso made on the breville bambino plus espresso machine in a shot glass with the wacaco exagram pro scale" /><figcaption>Underextracted<small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/z6zBaBfELEYmGUx7r2isY5.png" alt="A photo of espresso made on the breville bambino plus espresso machine in a shot glass with the wacaco exagram pro scale" /><figcaption>Perfect<small role="credit">Erin Bashford</small></figcaption></figure></figure><p>Both are bad! </p><p>Overextraction is where the coffee has spent too much time with water or pressure, resulting in a burned, bitter coffee. You have probably ground your coffee too coarsely.</p><p>Underextraction is where the coffee hasn't spent enough time with water or pressure, making a sour, acidic coffee. You have probably ground your coffee too finely. </p><p>If your coffee is coming out really slowly, that's overextraction. This will taste incredibly intense and burned, like charcoal. </p><p>If your coffee is coming out quickly, that's underextraction. This will taste like lemon juice. Nasty!</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-exV2aO"></div>                            </div>                            <script src="https://kwizly.com/embed/exV2aO.js" async></script><p>Did I answer all of your jargon-related questions? Let me know in the comments if you want me to explain anything else. </p><p>I release new episodes of The Coffee Lab every Friday, so be sure to check back next week for the next coffee adventure.</p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-the-new-nespresso-summer-flavors-heres-my-favorite"><strong>I tried the new Nespresso summer flavors — here's my favorite</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/im-an-ex-starbucks-barista-you-can-make-the-iced-brown-sugar-oatmilk-shaken-espresso-at-home-for-a-fraction-of-the-cost"><strong>I'm an ex-Starbucks barista — you can make the Iced Brown Sugar Oatmilk Shaken Espresso at home for a fraction of the cost</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Amazon just slashed prices on Ninja's best kitchen appliances — my 15 favorite deals start at $59 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Ninja makes some of my favorite kitchen appliances on the market — and right now, Amazon is offering some pretty impressive deals on the top-rated gadgets! You can score big savings on summer must-haves like coolers, slushy machines, grills, air fryers and much more.</p><p>Whether you're firing up the grill for a barbecue or you're hoping to simplify your weekly meal prep, <a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B?" target="_blank" rel="nofollow">Ninja is slashing up to 40% off its best-selling kitchen appliances</a>. And the best part? The deals start at just $59 and are the perfect finds for summer.</p><p>If you're in the market for some new cooking appliances, take it from me: Ninja is <em>the</em> brand to buy. Keep scrolling to check out my favorite Ninja picks from Amazon's sale. </p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><strong></strong><a href="https://www.sharkninja.com/sales-offers/shop-all-sale" target="_blank" rel="nofollow"><strong>shop all Ninja deals at Ninja</strong></a></li><li><a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow"><strong>shop all Ninja deals at Amazon</strong></a></li><li><strong>Ninja Blast Portable Blender: </strong><a href="https://www.amazon.com/dp/B0D2ZBNG9H?" target="_blank" rel="nofollow"><strong>was $69 now $59</strong></a></li><li><strong>Ninja Professional Blender: </strong><a href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B00NGV4506?" target="_blank" rel="nofollow"><strong>was $109 now $89</strong></a><strong></strong></li><li><strong>Ninja Iced Coffee Maker: </strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0C25Y15TL?" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Ninja Backpack Cooler: </strong><a href="https://www.amazon.com/Ninja-Backpack-FrostVault-Leakproof-FF124CS/dp/B0F4Y5G8GF" target="_blank" rel="nofollow"><strong>was $199 now $179</strong></a></li><li><strong>Ninja Mega Kitchen System: </strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><strong>was $219 now $179</strong></a></li><li><strong>Ninja FrostVault Cooler, 45qt: </strong><a href="https://www.amazon.com/Ninja-FrostVault-Insulated-Storage-FB245MR/dp/B0DPN8XSP3?" target="_blank" rel="nofollow"><strong>was $349 now $279</strong></a></li></ul><h3 class="article-body__section" id="section-ninja-deals"><span>Ninja Deals</span></h3><div class="product"><a data-dimension112="b6666144-90e6-11f1-9445-2586d3bf1a20" data-action="Deal Block" data-label="Blast Portable Blender" data-dimension48="Blast Portable Blender" data-dimension25="$59" href="https://www.amazon.com/dp/B0D2ZBNG9H?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:170.40%;"><img id="FCnJUW6WAsjjZShgBrxUa9" name="Ninja Blast.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FCnJUW6WAsjjZShgBrxUa9.jpg" mos="" align="middle" fullscreen="" width="679" height="1157" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0D2ZBNG9H?" target="_blank" rel="nofollow" data-dimension112="b6666144-90e6-11f1-9445-2586d3bf1a20" data-action="Deal Block" data-label="Blast Portable Blender" data-dimension48="Blast Portable Blender" data-dimension25="$59">Blast Portable Blender: was $69 now $59</a></strong><br>We gave the <a href="https://www.tomsguide.com/home/home-appliances/ninja-blast-max-review">Blast a 4-star rating in our review</a>. While portable blenders struggle to compete with larger models, there's no denying that this portable model punches well above its weight in price and features. For $59, it's an absolute bargain.<a class="view-deal button" href="https://www.amazon.com/dp/B0D2ZBNG9H?" target="_blank" rel="nofollow" data-dimension112="b6666144-90e6-11f1-9445-2586d3bf1a20" data-action="Deal Block" data-label="Blast Portable Blender" data-dimension48="Blast Portable Blender" data-dimension25="$59">View Deal</a></p></div><div class="product"><a data-dimension112="b66661bc-90e6-11f1-81ec-df578338b884" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$89" href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B00NGV4506?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:162.30%;"><img id="sFftzctqbej7oaSQLEtBRj" name="Ninja Pro Blender" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sFftzctqbej7oaSQLEtBRj.jpg" mos="" align="middle" fullscreen="" width="679" height="1102" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B00NGV4506?" target="_blank" rel="nofollow" data-dimension112="b66661bc-90e6-11f1-81ec-df578338b884" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$89">Professional Blender: was $109 now $89</a></strong><br>Be it daily smoothies or making ice cream, this blender can tackle it all, at the touch of a button. Also featured on our <a href="https://www.tomsguide.com/best-picks/best-blenders">best blenders</a> guide, this comes with 3 preset Auto-iQ programs that creates smoothies, frozen drinks, and ice cream, and its powerful blades can crush ice in no time. This is a great deal if you want an easy-to-use blender, with professional results.  <a class="view-deal button" href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B00NGV4506?" target="_blank" rel="nofollow" data-dimension112="b66661bc-90e6-11f1-81ec-df578338b884" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$89">View Deal</a></p></div><div class="product"><a data-dimension112="b666622a-90e6-11f1-bd1b-1db52dba49c6" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99" href="https://www.amazon.com/dp/B0C25Y15TL?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:99.56%;"><img id="4uiaRXRKY8Go6F4ZpM7UdA" name="Ninja coffee" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4uiaRXRKY8Go6F4ZpM7UdA.jpg" mos="" align="middle" fullscreen="" width="679" height="676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0C25Y15TL?" target="_blank" rel="nofollow" data-dimension112="b666622a-90e6-11f1-bd1b-1db52dba49c6" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">Pods & Grounds Specialty Single-Serve Iced Coffee Maker: was $129 now $99</a></strong><br>This stylish Ninja coffee maker comes in a gorgeous Sage colorway and is now on sale for $30 off. Whether you prefer pods or grounds, this device makes tasty brews in 7 sizes (6-oz. to 24-oz) and has a built-in, fold-away milk frother!<a class="view-deal button" href="https://www.amazon.com/dp/B0C25Y15TL?" target="_blank" rel="nofollow" data-dimension112="b666622a-90e6-11f1-bd1b-1db52dba49c6" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="b66662a2-90e6-11f1-bc72-d9463eb6fedb" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109" href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="JkaewDaMJCoHarzBbjk5u8" name="Ninja BlendBOSS deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/JkaewDaMJCoHarzBbjk5u8.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow" data-dimension112="b66662a2-90e6-11f1-bc72-d9463eb6fedb" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109">BlendBOSS Blender: was $129 now $109</a></strong><br>The Ninja BlendBOSS is one of the best personal blenders we've used. In our <a href="https://www.tomsguide.com/home/home-appliances/ninja-blendboss-review">Ninja BlendBOSS review</a>, we said the Editor's Choice blender is great at everything from making hummus to lump-free smoothies. Its' compact, comes in a range of colors, and it's simply the best personal blender you can buy at this price point. <a class="view-deal button" href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow" data-dimension112="b66662a2-90e6-11f1-bc72-d9463eb6fedb" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109">View Deal</a></p></div><div class="product"><a data-dimension112="b6666310-90e6-11f1-b1bf-834177ef4845" data-action="Deal Block" data-label="Flip Toaster Oven & Air Fryer" data-dimension48="Flip Toaster Oven & Air Fryer" data-dimension25="$149" href="https://www.amazon.com/Ninja-Functionality-Capability-Removable-SP151/dp/B0D1CXL52G" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:600px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="qey9zt42Hnq8ePgcaiiaR7" name="Ninja Flip" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qey9zt42Hnq8ePgcaiiaR7.jpg" mos="" align="middle" fullscreen="" width="600" height="600" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Functionality-Capability-Removable-SP151/dp/B0D1CXL52G" target="_blank" rel="nofollow" data-dimension112="b6666310-90e6-11f1-b1bf-834177ef4845" data-action="Deal Block" data-label="Flip Toaster Oven & Air Fryer" data-dimension48="Flip Toaster Oven & Air Fryer" data-dimension25="$149">Flip Toaster Oven & Air Fryer: was $249 now $149</a></strong><br>The Ninja Flip stainless steel model is also on sale. It has 8 functions in one convenient and versatile machine: air fry, roast, bake, toast, and more. When you're not using it, you can flip it up and away, which will give you 50% more counter space back.<a class="view-deal button" href="https://www.amazon.com/Ninja-Functionality-Capability-Removable-SP151/dp/B0D1CXL52G" target="_blank" rel="nofollow" data-dimension112="b6666310-90e6-11f1-b1bf-834177ef4845" data-action="Deal Block" data-label="Flip Toaster Oven & Air Fryer" data-dimension48="Flip Toaster Oven & Air Fryer" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b666637e-90e6-11f1-88ef-a5432b0ffbe6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension25="$159" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:85.86%;"><img id="zNkXNCVYCubXhCRwjaoGPj" name="Ninjs Crispi" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zNkXNCVYCubXhCRwjaoGPj.jpg" mos="" align="middle" fullscreen="" width="679" height="583" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="b666637e-90e6-11f1-88ef-a5432b0ffbe6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension25="$159">Crispi 4-in-1 Portable Glass Air Fryer Cooking System: was $179 now $159</a></strong><br>The Ninja Crispi is one of Ninja's most interesting releases. It features a removable cooking lid and interchangeable glass containers to cook meals of a range of sizes. In our <a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">Ninja Crispi review</a>, it earned a well-deserved 4.5 stars.  <a class="view-deal button" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="b666637e-90e6-11f1-88ef-a5432b0ffbe6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension25="$159">View Deal</a></p></div><div class="product"><a data-dimension112="b66663f6-90e6-11f1-bae6-83bce4e1c3b4" data-action="Deal Block" data-label="Backpack Cooler" data-dimension48="Backpack Cooler" data-dimension25="$179" href="https://www.amazon.com/Ninja-Backpack-FrostVault-Leakproof-FF124CS/dp/B0F4Y5G8GF" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:464px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="WW5AWCoTo6gey3CoZ7CHzk" name="Screenshot 2025-07-07 153631" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WW5AWCoTo6gey3CoZ7CHzk.jpg" mos="" align="middle" fullscreen="" width="464" height="464" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Ninja's backpack cooler is perfect for heading to the beach or having a picnic. It has a built-in cold dry zone to keep food dry and separate at under 40°F, holds ice for up to 24 hours, and has a top lid and side opening for easier access. It holds up to 24 cans, or 16 cans with ice. It even have a built in bottle opener.<a class="view-deal button" href="https://www.amazon.com/Ninja-Backpack-FrostVault-Leakproof-FF124CS/dp/B0F4Y5G8GF" target="_blank" rel="nofollow" data-dimension112="b66663f6-90e6-11f1-bae6-83bce4e1c3b4" data-action="Deal Block" data-label="Backpack Cooler" data-dimension48="Backpack Cooler" data-dimension25="$179">View Deal</a></p></div><div class="product"><a data-dimension112="b6666464-90e6-11f1-ba87-557c5b25b806" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1676px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="sWibiMn9jcqpLxyQRazy6f" name="Ninja Amazon 2.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sWibiMn9jcqpLxyQRazy6f.jpg" mos="" align="middle" fullscreen="" width="1676" height="1676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="b6666464-90e6-11f1-ba87-557c5b25b806" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179">Mega Kitchen System: was $219 now $179</a></strong><br>Leave all the multi-tasking in the kitchen to one appliance with this Ninja blender. With four functions for smoothies, processing, dough, drinks and more it will quickly become your go-to kitchen appliance. With this ultimate appliance, you’ll be enjoying fresh, healthy drinks, homemade bread and sweet bakes.<a class="view-deal button" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="b6666464-90e6-11f1-ba87-557c5b25b806" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179">View Deal</a></p></div><div class="product"><a data-dimension112="b66664d2-90e6-11f1-947c-7533996d7154" data-action="Deal Block" data-label="Foodi DualZone Air Fryer (DZ401)" data-dimension48="Foodi DualZone Air Fryer (DZ401)" data-dimension25="$199" href="https://www.amazon.com/Ninja-DZ401-Independent-Dehydrate-Family-Sized/dp/B0B15Q3HCQ?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1952px;"><p class="vanilla-image-block" style="padding-top:66.91%;"><img id="gibkomA9CKgWqZ8r2NX7f8" name="Ninja DZ401 Foodi" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/gibkomA9CKgWqZ8r2NX7f8.png" mos="" align="middle" fullscreen="" width="1952" height="1306" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-DZ401-Independent-Dehydrate-Family-Sized/dp/B0B15Q3HCQ?" target="_blank" rel="nofollow" data-dimension112="b66664d2-90e6-11f1-947c-7533996d7154" data-action="Deal Block" data-label="Foodi DualZone Air Fryer (DZ401)" data-dimension48="Foodi DualZone Air Fryer (DZ401)" data-dimension25="$199">Foodi DualZone Air Fryer (DZ401): was $229 now $199</a></strong><br>This relatively affordable dual-zone air fryer is one of Ninja's best. It's got the perks of SmartFinish and MatchCook, which will allow you to either sync two cooking settings to finish at the same time, or duplicate your settings to the other basket and maximize your space. <a class="view-deal button" href="https://www.amazon.com/Ninja-DZ401-Independent-Dehydrate-Family-Sized/dp/B0B15Q3HCQ?" target="_blank" rel="nofollow" data-dimension112="b66664d2-90e6-11f1-947c-7533996d7154" data-action="Deal Block" data-label="Foodi DualZone Air Fryer (DZ401)" data-dimension48="Foodi DualZone Air Fryer (DZ401)" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="b6666540-90e6-11f1-a4c4-ab34db796060" data-action="Deal Block" data-label="FrostVault 30QT" data-dimension48="FrostVault 30QT" data-dimension25="$229" href="https://www.amazon.com/dp/B0DPNCDYCZ?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1509px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="zcrSfDSQ58GkQ5fatetyy9" name="1751644471.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zcrSfDSQ58GkQ5fatetyy9.jpg" mos="" align="middle" fullscreen="" width="1509" height="1509" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0DPNCDYCZ?" target="_blank" rel="nofollow" data-dimension112="b6666540-90e6-11f1-a4c4-ab34db796060" data-action="Deal Block" data-label="FrostVault 30QT" data-dimension48="FrostVault 30QT" data-dimension25="$229">FrostVault 30QT: was $279 now $229</a></strong><br>If you don't think you need a massive cooler, you can still get a good deal on the 30QT model. This will fit 48 cans without ice or 28 cans with ice. It still keeps everything cold for days at a time, it's just smaller. It also has the cool dry zone, which lets you keep food and other items cool without all the moisture.<a class="view-deal button" href="https://www.amazon.com/dp/B0DPNCDYCZ?" target="_blank" rel="nofollow" data-dimension112="b6666540-90e6-11f1-a4c4-ab34db796060" data-action="Deal Block" data-label="FrostVault 30QT" data-dimension48="FrostVault 30QT" data-dimension25="$229">View Deal</a></p></div><div class="product"><a data-dimension112="b66665b8-90e6-11f1-a04a-438a5166aaf2" data-action="Deal Block" data-label="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension48="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension25="$249" href="https://www.amazon.com/Ninja-Countertop-Convection-Stainless-DT501/dp/B0D4R7DMYN?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:76.88%;"><img id="KybxnS8GerWMHfrZ6mQdvm" name="Ninja Air Fryer Toaster" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/KybxnS8GerWMHfrZ6mQdvm.jpg" mos="" align="middle" fullscreen="" width="679" height="522" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Countertop-Convection-Stainless-DT501/dp/B0D4R7DMYN?" target="_blank" rel="nofollow" data-dimension112="b66665b8-90e6-11f1-a04a-438a5166aaf2" data-action="Deal Block" data-label="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension48="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension25="$249">Double Stack XL Air Fryer & Toaster Oven Combo: was $429 now $249</a></strong><br>When we got our hands on the <a href="https://www.tomsguide.com/home/home-appliances/ninja-prestige-smart-xl-air-fryer-oven-dt501-review">Ninja Prestige Smart XL Oven</a>, we were super impressed by its capacity, range of cooking functions and intuitive, easy-to-use controls. It's a great alternative for anyone not wanting a full-size oven or an XL air fryer in their kitchen. And right now the <a href="https://www.tomsguide.com/best-picks/best-toaster-ovens">best toaster oven</a> is $180 off. <a class="view-deal button" href="https://www.amazon.com/Ninja-Countertop-Convection-Stainless-DT501/dp/B0D4R7DMYN?" target="_blank" rel="nofollow" data-dimension112="b66665b8-90e6-11f1-a04a-438a5166aaf2" data-action="Deal Block" data-label="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension48="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension25="$249">View Deal</a></p></div><div class="product"><a data-dimension112="b6666626-90e6-11f1-b634-7b3f9ca7411a" data-action="Deal Block" data-label="FrostVault Cooler, 65qt" data-dimension48="FrostVault Cooler, 65qt" data-dimension25="$279" href="https://www.amazon.com/dp/B0DPN8XSP3?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:80.67%;"><img id="Aq25cPGp35tFCUDNNanm9A" name="FrostVault Cooler, 45-quart" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Aq25cPGp35tFCUDNNanm9A.jpg" mos="" align="middle" fullscreen="" width="1500" height="1210" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0DPN8XSP3?" target="_blank" rel="nofollow" data-dimension112="b6666626-90e6-11f1-b634-7b3f9ca7411a" data-action="Deal Block" data-label="FrostVault Cooler, 65qt" data-dimension48="FrostVault Cooler, 65qt" data-dimension25="$279">FrostVault Cooler, 65qt: was $349 now $279</a></strong><br>The Ninja FrostVault cooler is a real star. With 65-quarts of capacity it can hold 97 cans, or 54 cans with ice, and will keep your food and beverages chilled for days. It also has a handy dry storage drawer, compartments to keep food separate, and 'all-terrain' wheels, It's a premium cooler that's worth snapping up.<a class="view-deal button" href="https://www.amazon.com/dp/B0DPN8XSP3?" target="_blank" rel="nofollow" data-dimension112="b6666626-90e6-11f1-b634-7b3f9ca7411a" data-action="Deal Block" data-label="FrostVault Cooler, 65qt" data-dimension48="FrostVault Cooler, 65qt" data-dimension25="$279">View Deal</a></p></div><div class="product"><a data-dimension112="b6666694-90e6-11f1-b7b3-b12bf3324e1e" data-action="Deal Block" data-label="Slushi Max Frozen Drink Maker" data-dimension48="Slushi Max Frozen Drink Maker" data-dimension25="$259" href="https://www.amazon.com/Ninja-FS301-Professional-RapidChill-Technology/dp/B0D2LZYQ2M?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:559px;"><p class="vanilla-image-block" style="padding-top:105.72%;"><img id="E3anx5fYk4a8xePxhgx2pZ" name="Ninja Slushi Max" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/E3anx5fYk4a8xePxhgx2pZ.png" mos="" align="middle" fullscreen="" width="559" height="591" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-FS301-Professional-RapidChill-Technology/dp/B0D2LZYQ2M?" target="_blank" rel="nofollow" data-dimension112="b6666694-90e6-11f1-b7b3-b12bf3324e1e" data-action="Deal Block" data-label="Slushi Max Frozen Drink Maker" data-dimension48="Slushi Max Frozen Drink Maker" data-dimension25="$259">Slushi Max Frozen Drink Maker: was $349 now $259</a></strong><br>The Slushi Max has a 88oz capacity that can make up to 7+drinks in one serving, making it ideal for hosting anytime of year. It's a relatively new release from Ninja, so this $90 discount is surprisingly generous. <a class="view-deal button" href="https://www.amazon.com/Ninja-FS301-Professional-RapidChill-Technology/dp/B0D2LZYQ2M?" target="_blank" rel="nofollow" data-dimension112="b6666694-90e6-11f1-b7b3-b12bf3324e1e" data-action="Deal Block" data-label="Slushi Max Frozen Drink Maker" data-dimension48="Slushi Max Frozen Drink Maker" data-dimension25="$259">View Deal</a></p></div><div class="product"><a data-dimension112="b6666702-90e6-11f1-81e2-65dff613702d" data-action="Deal Block" data-label="Woodfire Pizza Oven" data-dimension48="Woodfire Pizza Oven" data-dimension25="$299" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1666px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="W5gcaBRuDdjvCedzhvKyej" name="Outdoor oven.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/W5gcaBRuDdjvCedzhvKyej.jpg" mos="" align="middle" fullscreen="" width="1666" height="1666" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3" target="_blank" rel="nofollow" data-dimension112="b6666702-90e6-11f1-81e2-65dff613702d" data-action="Deal Block" data-label="Woodfire Pizza Oven" data-dimension48="Woodfire Pizza Oven" data-dimension25="$299">Woodfire Pizza Oven: was $399 now $299</a></strong><br>If you’re a fan of cooking outdoors, this is the deal for you. This outdoor pizza oven offers eight functions, including pizza, max roast, specialty roast and smoker amongst others, so it’s by no means limited to just pizza. Powered by electricity, it can reach up to 700°F, plus there’s the option to add wood pellets if you want smoked food too. The capacity can hold a 12 lb turkey or a 12-inch pizza, which is generous enough. <a class="view-deal button" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3" target="_blank" rel="nofollow" data-dimension112="b6666702-90e6-11f1-81e2-65dff613702d" data-action="Deal Block" data-label="Woodfire Pizza Oven" data-dimension48="Woodfire Pizza Oven" data-dimension25="$299">View Deal</a></p></div><div class="product"><a data-dimension112="b6666770-90e6-11f1-ae55-77f3c466d6fb" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999" href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0DSY91FF8?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="iaK255T8K7gB57X7ZEG77H" name="PG301_WEBR_GalleryImages_1 (1) copy" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/iaK255T8K7gB57X7ZEG77H.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0DSY91FF8?" target="_blank" rel="nofollow" data-dimension112="b6666770-90e6-11f1-ae55-77f3c466d6fb" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999">FlexFlame Propane Grill: was $1119 now $999</a></strong><br>The Ninja FlexFlame is Ninja's first attempt at a propane grill, and it offers all the ingeniuity you'd expect from a Ninja product. It's got a pellet chamber to infuse your food with an authentic smoked flavor, but can also sear and even be converted to a griddle with the use of add-ons.<a class="view-deal button" href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0DSY91FF8?" target="_blank" rel="nofollow" data-dimension112="b6666770-90e6-11f1-ae55-77f3c466d6fb" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999">View Deal</a></p></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/amazon-just-slashed-prices-on-ninjas-best-kitchen-appliances-my-15-favorite-deals-start-at-usd59</link>
                                                                            <description>
                            <![CDATA[ Amazon is knocking up to 40% off Ninja kitchen appliances. Check out my favorite deals on air fryers, grills, blenders, coolers and more. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">LaaFsVxTyXdC5h93KMrtTa</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/EohkcmcnKPPxAavEYyuLuj-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 06 Aug 2026 12:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                                    <dc:creator><![CDATA[ Olivia Halevy ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/YAg7Fmh9dGKbg3hULBMWiU.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Olivia is a Deals Writer at Tom’s Guide. She loves all things lifestyle, shopping and sourcing the best deals from some of the top brands, retailers and websites around. From fashion and beauty to smart home and tech, she has an eye for finding practical, stylish, and reasonably-priced items that help make life simpler (and a little more aesthetically-pleasing).&amp;nbsp;Olivia has over five years of experience writing lifestyle, commerce, and branded content for &lt;a href=&quot;http://patch.com/&quot;&gt;Patch.com&lt;/a&gt;. She&#039;s covered major shopping holidays like Amazon Prime Day, Black Friday and Cyber Monday. In her free time, she enjoys trying out new exercise classes, binge-watching the latest viral TV shows, cooking, spending time with family and taking long walks with her dog, Gus.&amp;nbsp;&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/EohkcmcnKPPxAavEYyuLuj-1280-80.jpg">
                                                            <media:credit><![CDATA[Amazon]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Ninja]]></media:description>                                                            <media:text><![CDATA[Ninja]]></media:text>
                                <media:title type="plain"><![CDATA[Ninja]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/EohkcmcnKPPxAavEYyuLuj-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Ninja makes some of my favorite kitchen appliances on the market — and right now, Amazon is offering some pretty impressive deals on the top-rated gadgets! You can score big savings on summer must-haves like coolers, slushy machines, grills, air fryers and much more.</p><p>Whether you're firing up the grill for a barbecue or you're hoping to simplify your weekly meal prep, <a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B?" target="_blank" rel="nofollow">Ninja is slashing up to 40% off its best-selling kitchen appliances</a>. And the best part? The deals start at just $59 and are the perfect finds for summer.</p><p>If you're in the market for some new cooking appliances, take it from me: Ninja is <em>the</em> brand to buy. Keep scrolling to check out my favorite Ninja picks from Amazon's sale. </p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><strong></strong><a href="https://www.sharkninja.com/sales-offers/shop-all-sale" target="_blank" rel="nofollow"><strong>shop all Ninja deals at Ninja</strong></a></li><li><a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow"><strong>shop all Ninja deals at Amazon</strong></a></li><li><strong>Ninja Blast Portable Blender: </strong><a href="https://www.amazon.com/dp/B0D2ZBNG9H?" target="_blank" rel="nofollow"><strong>was $69 now $59</strong></a></li><li><strong>Ninja Professional Blender: </strong><a href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B00NGV4506?" target="_blank" rel="nofollow"><strong>was $109 now $89</strong></a><strong></strong></li><li><strong>Ninja Iced Coffee Maker: </strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0C25Y15TL?" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Ninja Backpack Cooler: </strong><a href="https://www.amazon.com/Ninja-Backpack-FrostVault-Leakproof-FF124CS/dp/B0F4Y5G8GF" target="_blank" rel="nofollow"><strong>was $199 now $179</strong></a></li><li><strong>Ninja Mega Kitchen System: </strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><strong>was $219 now $179</strong></a></li><li><strong>Ninja FrostVault Cooler, 45qt: </strong><a href="https://www.amazon.com/Ninja-FrostVault-Insulated-Storage-FB245MR/dp/B0DPN8XSP3?" target="_blank" rel="nofollow"><strong>was $349 now $279</strong></a></li></ul><h3 class="article-body__section" id="section-ninja-deals"><span>Ninja Deals</span></h3><div class="product"><a data-dimension112="b6666144-90e6-11f1-9445-2586d3bf1a20" data-action="Deal Block" data-label="Blast Portable Blender" data-dimension48="Blast Portable Blender" data-dimension25="$59" href="https://www.amazon.com/dp/B0D2ZBNG9H?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:170.40%;"><img id="FCnJUW6WAsjjZShgBrxUa9" name="Ninja Blast.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FCnJUW6WAsjjZShgBrxUa9.jpg" mos="" align="middle" fullscreen="" width="679" height="1157" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0D2ZBNG9H?" target="_blank" rel="nofollow" data-dimension112="b6666144-90e6-11f1-9445-2586d3bf1a20" data-action="Deal Block" data-label="Blast Portable Blender" data-dimension48="Blast Portable Blender" data-dimension25="$59">Blast Portable Blender: was $69 now $59</a></strong><br>We gave the <a href="https://www.tomsguide.com/home/home-appliances/ninja-blast-max-review">Blast a 4-star rating in our review</a>. While portable blenders struggle to compete with larger models, there's no denying that this portable model punches well above its weight in price and features. For $59, it's an absolute bargain.<a class="view-deal button" href="https://www.amazon.com/dp/B0D2ZBNG9H?" target="_blank" rel="nofollow" data-dimension112="b6666144-90e6-11f1-9445-2586d3bf1a20" data-action="Deal Block" data-label="Blast Portable Blender" data-dimension48="Blast Portable Blender" data-dimension25="$59">View Deal</a></p></div><div class="product"><a data-dimension112="b66661bc-90e6-11f1-81ec-df578338b884" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$89" href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B00NGV4506?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:162.30%;"><img id="sFftzctqbej7oaSQLEtBRj" name="Ninja Pro Blender" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sFftzctqbej7oaSQLEtBRj.jpg" mos="" align="middle" fullscreen="" width="679" height="1102" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B00NGV4506?" target="_blank" rel="nofollow" data-dimension112="b66661bc-90e6-11f1-81ec-df578338b884" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$89">Professional Blender: was $109 now $89</a></strong><br>Be it daily smoothies or making ice cream, this blender can tackle it all, at the touch of a button. Also featured on our <a href="https://www.tomsguide.com/best-picks/best-blenders">best blenders</a> guide, this comes with 3 preset Auto-iQ programs that creates smoothies, frozen drinks, and ice cream, and its powerful blades can crush ice in no time. This is a great deal if you want an easy-to-use blender, with professional results.  <a class="view-deal button" href="https://www.amazon.com/Ninja-BR201AMZ-Professional-Smoothies-Dishwasher-Safe/dp/B00NGV4506?" target="_blank" rel="nofollow" data-dimension112="b66661bc-90e6-11f1-81ec-df578338b884" data-action="Deal Block" data-label="Professional Blender" data-dimension48="Professional Blender" data-dimension25="$89">View Deal</a></p></div><div class="product"><a data-dimension112="b666622a-90e6-11f1-bd1b-1db52dba49c6" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99" href="https://www.amazon.com/dp/B0C25Y15TL?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:99.56%;"><img id="4uiaRXRKY8Go6F4ZpM7UdA" name="Ninja coffee" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4uiaRXRKY8Go6F4ZpM7UdA.jpg" mos="" align="middle" fullscreen="" width="679" height="676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0C25Y15TL?" target="_blank" rel="nofollow" data-dimension112="b666622a-90e6-11f1-bd1b-1db52dba49c6" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">Pods & Grounds Specialty Single-Serve Iced Coffee Maker: was $129 now $99</a></strong><br>This stylish Ninja coffee maker comes in a gorgeous Sage colorway and is now on sale for $30 off. Whether you prefer pods or grounds, this device makes tasty brews in 7 sizes (6-oz. to 24-oz) and has a built-in, fold-away milk frother!<a class="view-deal button" href="https://www.amazon.com/dp/B0C25Y15TL?" target="_blank" rel="nofollow" data-dimension112="b666622a-90e6-11f1-bd1b-1db52dba49c6" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="b66662a2-90e6-11f1-bc72-d9463eb6fedb" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109" href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="JkaewDaMJCoHarzBbjk5u8" name="Ninja BlendBOSS deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/JkaewDaMJCoHarzBbjk5u8.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow" data-dimension112="b66662a2-90e6-11f1-bc72-d9463eb6fedb" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109">BlendBOSS Blender: was $129 now $109</a></strong><br>The Ninja BlendBOSS is one of the best personal blenders we've used. In our <a href="https://www.tomsguide.com/home/home-appliances/ninja-blendboss-review">Ninja BlendBOSS review</a>, we said the Editor's Choice blender is great at everything from making hummus to lump-free smoothies. Its' compact, comes in a range of colors, and it's simply the best personal blender you can buy at this price point. <a class="view-deal button" href="https://www.amazon.com/Ninja-BlendbossTM-Detachable-Technology-DB351CY/dp/B0FSGSYF79?" target="_blank" rel="nofollow" data-dimension112="b66662a2-90e6-11f1-bc72-d9463eb6fedb" data-action="Deal Block" data-label="BlendBOSS Blender" data-dimension48="BlendBOSS Blender" data-dimension25="$109">View Deal</a></p></div><div class="product"><a data-dimension112="b6666310-90e6-11f1-b1bf-834177ef4845" data-action="Deal Block" data-label="Flip Toaster Oven & Air Fryer" data-dimension48="Flip Toaster Oven & Air Fryer" data-dimension25="$149" href="https://www.amazon.com/Ninja-Functionality-Capability-Removable-SP151/dp/B0D1CXL52G" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:600px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="qey9zt42Hnq8ePgcaiiaR7" name="Ninja Flip" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qey9zt42Hnq8ePgcaiiaR7.jpg" mos="" align="middle" fullscreen="" width="600" height="600" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Functionality-Capability-Removable-SP151/dp/B0D1CXL52G" target="_blank" rel="nofollow" data-dimension112="b6666310-90e6-11f1-b1bf-834177ef4845" data-action="Deal Block" data-label="Flip Toaster Oven & Air Fryer" data-dimension48="Flip Toaster Oven & Air Fryer" data-dimension25="$149">Flip Toaster Oven & Air Fryer: was $249 now $149</a></strong><br>The Ninja Flip stainless steel model is also on sale. It has 8 functions in one convenient and versatile machine: air fry, roast, bake, toast, and more. When you're not using it, you can flip it up and away, which will give you 50% more counter space back.<a class="view-deal button" href="https://www.amazon.com/Ninja-Functionality-Capability-Removable-SP151/dp/B0D1CXL52G" target="_blank" rel="nofollow" data-dimension112="b6666310-90e6-11f1-b1bf-834177ef4845" data-action="Deal Block" data-label="Flip Toaster Oven & Air Fryer" data-dimension48="Flip Toaster Oven & Air Fryer" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b666637e-90e6-11f1-88ef-a5432b0ffbe6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension25="$159" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:85.86%;"><img id="zNkXNCVYCubXhCRwjaoGPj" name="Ninjs Crispi" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zNkXNCVYCubXhCRwjaoGPj.jpg" mos="" align="middle" fullscreen="" width="679" height="583" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="b666637e-90e6-11f1-88ef-a5432b0ffbe6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension25="$159">Crispi 4-in-1 Portable Glass Air Fryer Cooking System: was $179 now $159</a></strong><br>The Ninja Crispi is one of Ninja's most interesting releases. It features a removable cooking lid and interchangeable glass containers to cook meals of a range of sizes. In our <a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">Ninja Crispi review</a>, it earned a well-deserved 4.5 stars.  <a class="view-deal button" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="b666637e-90e6-11f1-88ef-a5432b0ffbe6" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer Cooking System" data-dimension25="$159">View Deal</a></p></div><div class="product"><a data-dimension112="b66663f6-90e6-11f1-bae6-83bce4e1c3b4" data-action="Deal Block" data-label="Backpack Cooler" data-dimension48="Backpack Cooler" data-dimension25="$179" href="https://www.amazon.com/Ninja-Backpack-FrostVault-Leakproof-FF124CS/dp/B0F4Y5G8GF" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:464px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="WW5AWCoTo6gey3CoZ7CHzk" name="Screenshot 2025-07-07 153631" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WW5AWCoTo6gey3CoZ7CHzk.jpg" mos="" align="middle" fullscreen="" width="464" height="464" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Ninja's backpack cooler is perfect for heading to the beach or having a picnic. It has a built-in cold dry zone to keep food dry and separate at under 40°F, holds ice for up to 24 hours, and has a top lid and side opening for easier access. It holds up to 24 cans, or 16 cans with ice. It even have a built in bottle opener.<a class="view-deal button" href="https://www.amazon.com/Ninja-Backpack-FrostVault-Leakproof-FF124CS/dp/B0F4Y5G8GF" target="_blank" rel="nofollow" data-dimension112="b66663f6-90e6-11f1-bae6-83bce4e1c3b4" data-action="Deal Block" data-label="Backpack Cooler" data-dimension48="Backpack Cooler" data-dimension25="$179">View Deal</a></p></div><div class="product"><a data-dimension112="b6666464-90e6-11f1-ba87-557c5b25b806" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1676px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="sWibiMn9jcqpLxyQRazy6f" name="Ninja Amazon 2.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sWibiMn9jcqpLxyQRazy6f.jpg" mos="" align="middle" fullscreen="" width="1676" height="1676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="b6666464-90e6-11f1-ba87-557c5b25b806" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179">Mega Kitchen System: was $219 now $179</a></strong><br>Leave all the multi-tasking in the kitchen to one appliance with this Ninja blender. With four functions for smoothies, processing, dough, drinks and more it will quickly become your go-to kitchen appliance. With this ultimate appliance, you’ll be enjoying fresh, healthy drinks, homemade bread and sweet bakes.<a class="view-deal button" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="b6666464-90e6-11f1-ba87-557c5b25b806" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$179">View Deal</a></p></div><div class="product"><a data-dimension112="b66664d2-90e6-11f1-947c-7533996d7154" data-action="Deal Block" data-label="Foodi DualZone Air Fryer (DZ401)" data-dimension48="Foodi DualZone Air Fryer (DZ401)" data-dimension25="$199" href="https://www.amazon.com/Ninja-DZ401-Independent-Dehydrate-Family-Sized/dp/B0B15Q3HCQ?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1952px;"><p class="vanilla-image-block" style="padding-top:66.91%;"><img id="gibkomA9CKgWqZ8r2NX7f8" name="Ninja DZ401 Foodi" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/gibkomA9CKgWqZ8r2NX7f8.png" mos="" align="middle" fullscreen="" width="1952" height="1306" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-DZ401-Independent-Dehydrate-Family-Sized/dp/B0B15Q3HCQ?" target="_blank" rel="nofollow" data-dimension112="b66664d2-90e6-11f1-947c-7533996d7154" data-action="Deal Block" data-label="Foodi DualZone Air Fryer (DZ401)" data-dimension48="Foodi DualZone Air Fryer (DZ401)" data-dimension25="$199">Foodi DualZone Air Fryer (DZ401): was $229 now $199</a></strong><br>This relatively affordable dual-zone air fryer is one of Ninja's best. It's got the perks of SmartFinish and MatchCook, which will allow you to either sync two cooking settings to finish at the same time, or duplicate your settings to the other basket and maximize your space. <a class="view-deal button" href="https://www.amazon.com/Ninja-DZ401-Independent-Dehydrate-Family-Sized/dp/B0B15Q3HCQ?" target="_blank" rel="nofollow" data-dimension112="b66664d2-90e6-11f1-947c-7533996d7154" data-action="Deal Block" data-label="Foodi DualZone Air Fryer (DZ401)" data-dimension48="Foodi DualZone Air Fryer (DZ401)" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="b6666540-90e6-11f1-a4c4-ab34db796060" data-action="Deal Block" data-label="FrostVault 30QT" data-dimension48="FrostVault 30QT" data-dimension25="$229" href="https://www.amazon.com/dp/B0DPNCDYCZ?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1509px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="zcrSfDSQ58GkQ5fatetyy9" name="1751644471.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zcrSfDSQ58GkQ5fatetyy9.jpg" mos="" align="middle" fullscreen="" width="1509" height="1509" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0DPNCDYCZ?" target="_blank" rel="nofollow" data-dimension112="b6666540-90e6-11f1-a4c4-ab34db796060" data-action="Deal Block" data-label="FrostVault 30QT" data-dimension48="FrostVault 30QT" data-dimension25="$229">FrostVault 30QT: was $279 now $229</a></strong><br>If you don't think you need a massive cooler, you can still get a good deal on the 30QT model. This will fit 48 cans without ice or 28 cans with ice. It still keeps everything cold for days at a time, it's just smaller. It also has the cool dry zone, which lets you keep food and other items cool without all the moisture.<a class="view-deal button" href="https://www.amazon.com/dp/B0DPNCDYCZ?" target="_blank" rel="nofollow" data-dimension112="b6666540-90e6-11f1-a4c4-ab34db796060" data-action="Deal Block" data-label="FrostVault 30QT" data-dimension48="FrostVault 30QT" data-dimension25="$229">View Deal</a></p></div><div class="product"><a data-dimension112="b66665b8-90e6-11f1-a04a-438a5166aaf2" data-action="Deal Block" data-label="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension48="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension25="$249" href="https://www.amazon.com/Ninja-Countertop-Convection-Stainless-DT501/dp/B0D4R7DMYN?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:76.88%;"><img id="KybxnS8GerWMHfrZ6mQdvm" name="Ninja Air Fryer Toaster" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/KybxnS8GerWMHfrZ6mQdvm.jpg" mos="" align="middle" fullscreen="" width="679" height="522" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Countertop-Convection-Stainless-DT501/dp/B0D4R7DMYN?" target="_blank" rel="nofollow" data-dimension112="b66665b8-90e6-11f1-a04a-438a5166aaf2" data-action="Deal Block" data-label="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension48="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension25="$249">Double Stack XL Air Fryer & Toaster Oven Combo: was $429 now $249</a></strong><br>When we got our hands on the <a href="https://www.tomsguide.com/home/home-appliances/ninja-prestige-smart-xl-air-fryer-oven-dt501-review">Ninja Prestige Smart XL Oven</a>, we were super impressed by its capacity, range of cooking functions and intuitive, easy-to-use controls. It's a great alternative for anyone not wanting a full-size oven or an XL air fryer in their kitchen. And right now the <a href="https://www.tomsguide.com/best-picks/best-toaster-ovens">best toaster oven</a> is $180 off. <a class="view-deal button" href="https://www.amazon.com/Ninja-Countertop-Convection-Stainless-DT501/dp/B0D4R7DMYN?" target="_blank" rel="nofollow" data-dimension112="b66665b8-90e6-11f1-a04a-438a5166aaf2" data-action="Deal Block" data-label="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension48="Double Stack XL Air Fryer & Toaster Oven Combo" data-dimension25="$249">View Deal</a></p></div><div class="product"><a data-dimension112="b6666626-90e6-11f1-b634-7b3f9ca7411a" data-action="Deal Block" data-label="FrostVault Cooler, 65qt" data-dimension48="FrostVault Cooler, 65qt" data-dimension25="$279" href="https://www.amazon.com/dp/B0DPN8XSP3?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:80.67%;"><img id="Aq25cPGp35tFCUDNNanm9A" name="FrostVault Cooler, 45-quart" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Aq25cPGp35tFCUDNNanm9A.jpg" mos="" align="middle" fullscreen="" width="1500" height="1210" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0DPN8XSP3?" target="_blank" rel="nofollow" data-dimension112="b6666626-90e6-11f1-b634-7b3f9ca7411a" data-action="Deal Block" data-label="FrostVault Cooler, 65qt" data-dimension48="FrostVault Cooler, 65qt" data-dimension25="$279">FrostVault Cooler, 65qt: was $349 now $279</a></strong><br>The Ninja FrostVault cooler is a real star. With 65-quarts of capacity it can hold 97 cans, or 54 cans with ice, and will keep your food and beverages chilled for days. It also has a handy dry storage drawer, compartments to keep food separate, and 'all-terrain' wheels, It's a premium cooler that's worth snapping up.<a class="view-deal button" href="https://www.amazon.com/dp/B0DPN8XSP3?" target="_blank" rel="nofollow" data-dimension112="b6666626-90e6-11f1-b634-7b3f9ca7411a" data-action="Deal Block" data-label="FrostVault Cooler, 65qt" data-dimension48="FrostVault Cooler, 65qt" data-dimension25="$279">View Deal</a></p></div><div class="product"><a data-dimension112="b6666694-90e6-11f1-b7b3-b12bf3324e1e" data-action="Deal Block" data-label="Slushi Max Frozen Drink Maker" data-dimension48="Slushi Max Frozen Drink Maker" data-dimension25="$259" href="https://www.amazon.com/Ninja-FS301-Professional-RapidChill-Technology/dp/B0D2LZYQ2M?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:559px;"><p class="vanilla-image-block" style="padding-top:105.72%;"><img id="E3anx5fYk4a8xePxhgx2pZ" name="Ninja Slushi Max" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/E3anx5fYk4a8xePxhgx2pZ.png" mos="" align="middle" fullscreen="" width="559" height="591" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-FS301-Professional-RapidChill-Technology/dp/B0D2LZYQ2M?" target="_blank" rel="nofollow" data-dimension112="b6666694-90e6-11f1-b7b3-b12bf3324e1e" data-action="Deal Block" data-label="Slushi Max Frozen Drink Maker" data-dimension48="Slushi Max Frozen Drink Maker" data-dimension25="$259">Slushi Max Frozen Drink Maker: was $349 now $259</a></strong><br>The Slushi Max has a 88oz capacity that can make up to 7+drinks in one serving, making it ideal for hosting anytime of year. It's a relatively new release from Ninja, so this $90 discount is surprisingly generous. <a class="view-deal button" href="https://www.amazon.com/Ninja-FS301-Professional-RapidChill-Technology/dp/B0D2LZYQ2M?" target="_blank" rel="nofollow" data-dimension112="b6666694-90e6-11f1-b7b3-b12bf3324e1e" data-action="Deal Block" data-label="Slushi Max Frozen Drink Maker" data-dimension48="Slushi Max Frozen Drink Maker" data-dimension25="$259">View Deal</a></p></div><div class="product"><a data-dimension112="b6666702-90e6-11f1-81e2-65dff613702d" data-action="Deal Block" data-label="Woodfire Pizza Oven" data-dimension48="Woodfire Pizza Oven" data-dimension25="$299" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1666px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="W5gcaBRuDdjvCedzhvKyej" name="Outdoor oven.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/W5gcaBRuDdjvCedzhvKyej.jpg" mos="" align="middle" fullscreen="" width="1666" height="1666" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3" target="_blank" rel="nofollow" data-dimension112="b6666702-90e6-11f1-81e2-65dff613702d" data-action="Deal Block" data-label="Woodfire Pizza Oven" data-dimension48="Woodfire Pizza Oven" data-dimension25="$299">Woodfire Pizza Oven: was $399 now $299</a></strong><br>If you’re a fan of cooking outdoors, this is the deal for you. This outdoor pizza oven offers eight functions, including pizza, max roast, specialty roast and smoker amongst others, so it’s by no means limited to just pizza. Powered by electricity, it can reach up to 700°F, plus there’s the option to add wood pellets if you want smoked food too. The capacity can hold a 12 lb turkey or a 12-inch pizza, which is generous enough. <a class="view-deal button" href="https://www.amazon.com/Ninja-Woodfire-Technology-terracotta-OO101/dp/B0C6BQVDX3" target="_blank" rel="nofollow" data-dimension112="b6666702-90e6-11f1-81e2-65dff613702d" data-action="Deal Block" data-label="Woodfire Pizza Oven" data-dimension48="Woodfire Pizza Oven" data-dimension25="$299">View Deal</a></p></div><div class="product"><a data-dimension112="b6666770-90e6-11f1-ae55-77f3c466d6fb" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999" href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0DSY91FF8?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="iaK255T8K7gB57X7ZEG77H" name="PG301_WEBR_GalleryImages_1 (1) copy" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/iaK255T8K7gB57X7ZEG77H.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0DSY91FF8?" target="_blank" rel="nofollow" data-dimension112="b6666770-90e6-11f1-ae55-77f3c466d6fb" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999">FlexFlame Propane Grill: was $1119 now $999</a></strong><br>The Ninja FlexFlame is Ninja's first attempt at a propane grill, and it offers all the ingeniuity you'd expect from a Ninja product. It's got a pellet chamber to infuse your food with an authentic smoked flavor, but can also sear and even be converted to a griddle with the use of add-ons.<a class="view-deal button" href="https://www.amazon.com/Ninja-FlexFlame-Electric-Stainless-PG301/dp/B0DSY91FF8?" target="_blank" rel="nofollow" data-dimension112="b6666770-90e6-11f1-ae55-77f3c466d6fb" data-action="Deal Block" data-label="FlexFlame Propane Grill" data-dimension48="FlexFlame Propane Grill" data-dimension25="$999">View Deal</a></p></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ KitchenAid KF2 review: this fully automatic coffee machine effortlessly takes the guesswork out of your morning brew ]]></title>
                                                                                                <dc:content><![CDATA[ <p>As a kitchen appliance reviewer, some of my favorite and top-rated products hail from KitchenAid. I've owned an Artisan Stand Mixer for years now, and use many KitchenAid appliances and accessories on a daily basis, so it's no surprise that when the opportunity arose to review the brand's new fully automatic coffee machine range, I volunteered immediately. </p><p>Now, I'm no stranger to a manual espresso maker — I love my Ninja Luxe Cafe more than anything else — so I really wanted to switch it up to see how a fully automatic machine compares to the manual control I'm used to. After all, these automatic coffee machines claim to deliver the perfect espresso in one touch, without worrying about grinds, milk or the like, so my curiosity was definitely piqued. (And spoiler alert: the KF2 did not disappoint). </p><p>Launched earlier this year, KitchenAid's new machines — the KF2, KF3 and KF4 — each have different functions, prices and colorways to suit every kind of one-touch coffee-lover. The machine reviewed here, the KitchenAid KF2, is the most affordable option and offers some hands-on time for hobbyist baristas who want manual control over their milk. </p><p>Over the course of a month, I put the KitchenAid KF2 through its paces and used it to make hot, iced and milk-based drinks.</p><h3 class="article-body__section" id="section-kitchenaid-kf2-review-specs"><span>KitchenAid KF2 review: Specs</span></h3><div ><table><caption>KitchenAid KF2 specs</caption><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p>$799.99 / £699 / AU$1,099</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>22.2lbs / 10.1kg</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>14.2"H × 7.7"W × 18.5"D / 36.07cm H × 19.56cm W × 46.99cm D</p></td></tr><tr><td class="firstcol " ><p><strong>Drink options</strong></p></td><td  ><p>6 – including espresso, coffee and over ice</p></td></tr><tr><td class="firstcol " ><p><strong>Grind settings</strong></p></td><td  ><p>5</p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>250g bean hopper & 250g ground coffee</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank</strong></p></td><td  ><p>1.8L</p></td></tr><tr><td class="firstcol " ><p><strong>Material</strong></p></td><td  ><p>Stainless steel</p></td></tr></tbody></table></div><h3 class="article-body__section" id="section-kitchenaid-kf2-review-price-and-availability"><span>KitchenAid KF2 review: Price and availability</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5492px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5kgtd9R3rJx4jo3k8ZHpK9" name="kf2-17" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/5kgtd9R3rJx4jo3k8ZHpK9.jpg" mos="" align="middle" fullscreen="" width="5492" height="3089" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>The KitchenAid KF2 in Australia, where I'm based, is priced at AU$1,099. For those in the UK, it's £699 and in the U.S., it's $799.99. When compared to the other coinciding releases of the KF3 ($999.99 / £749 / AU$1,349) and KF4 ($1,299.99 / £899 / AU$1,599), this is the "budget" or entry-level option in KitchenAid's new fully automatic lineup. </p><p>Now the word budget is a bit of a stretch — it's still pretty expensive — and it doesn't quite dazzle compared to similarly priced automatic machines. I’m talking largely about De’Longhi here, as you can often pick up one of the esteemed brand’s machines for less, in return for more extensive features, like one-touch drink presets and automatic milk-foaming systems. That said, the KF2 offers six drink presets and a steam wand (which some <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers#section-best-superautomatic">5-star automatic machines</a> neglect entirely). And, I won't lie, the KF2's sleek, matte black design is nice to look at. </p><h3 class="article-body__section" id="section-kitchenaid-kf2-review-design"><span>KitchenAid KF2 review: Design</span></h3><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/B42ouYAGfY9UcZkv8FjnA9.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/qf4jvodr7TjtuFH6MYEgi3.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/7DBniEP8J5Udvh5mDMLRa8.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/HMQ7WNqCQYUNbNY54LWtc7.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MoKzrMtAcqDguKeB6dAZQ8.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure></figure><p>KitchenAid's newest fully automatic machines, including the KF2, have a much smaller footprint than their predecessors. Measuring just 14.2" x 7.7" x 18.5" / 36.07cm x 19.56cm x 46.99cm, the KF2 takes up very little counterspace, making it ideal for those with smaller kitchens. The KF2 has five function buttons on the front of its backlit LED screen, as well as a power button on the right side. It also features a 1.8L water tank at the back, a Pannarello steam wand, a drip tray and a concealed 250g bean hopper. </p><p>Unlike the KF3 and KF4, which are both available in matte black and KitchenAid's signature Porcelain white, the KF2 is limited to the former. I quite like the simplicity of the black colorway, but it isn't as visually appealing as the white or Juniper green colorways that the KitchenAid KF8 offers, in my opinion. The stainless steel accents in the drip tray and the steam wand are a nice contrast to the matte machine body, but I didn't love the feel of the function buttons.</p><p>A fun surprise discovered during set up were two wheels under the machine, making it easy to move around if you needed to fill the water tank. It's also not that heavy, at just 22lbs / 10.1kg, so I could easily lift it and move it into place.</p><h3 class="article-body__section" id="section-kitchenaid-kf2-review-performance"><span>KitchenAid KF2 review: Performance</span></h3><h2 id="grinder">Grinder</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5399px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="z4MCQPoBhigQt97L7BzV98" name="kf2-19" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/z4MCQPoBhigQt97L7BzV98.jpg" mos="" align="middle" fullscreen="" width="5399" height="3037" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>The KF2 uses KitchenAid’s Intelligrind technology, which aims to take the guesswork out of selecting the best grind size for your chosen beans. For most users, it allows for a hands-off approach, just pour your beans into the top-mounted hopper and select a drink. If you do want to make some finer adjustments, you can open the machine's side panel, where you’ll find a slider to make the grind finer (to the left) or coarser (to the right). However, for most users, I don't think any adjustments would need to be made, as the automatic function takes away any manual hand tamping or grinding. </p><p>Regardless of whether you make adjustments or not, the KF2's grinder is so quiet. That's largely due to its Quiet Mark certification, meaning it won't make too much noise while you're brewing your early morning coffee. </p><p>If you prefer decaf or coffee grounds, the KF2 also has a 250g ground coffee section next to the bean hopper. It is worth noting that when you add in ground coffee, the machine will switch into its ground coffee mode and exclusively use it over any beans in the hopper. To fix this, you'll need to toggle the bean setting before every use. </p><h2 id="espresso">Espresso</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3815px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TWEoQeT5x7nJbUAns53bL5" name="kf2-9" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/TWEoQeT5x7nJbUAns53bL5.jpg" mos="" align="middle" fullscreen="" width="3815" height="2146" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>The KF2 can truly customise your coffee to your liking, thanks to its drink, strength and dosage settings. Once you choose your drink recipe — e.g. either an espresso, coffee or over ice — you can make your coffee or espresso shot as weak or strong as you'd like using the strength and dose buttons. If you make adjustments to the strength, the KF2 will grind fewer or more beans, resulting in a dose that’s between 7g and 14g.</p><p>Regardless of the strength setting you choose, the KF2 excels at pulling a consistently well-extracted shot of espresso. For the test brew, I used the double strength on the espresso setting to extract a single shot, and it was perfectly balanced. Just look at that crema! This was using medium-dark roast beans with notes of rich caramel, butterscotch and sweet red fruits. I'm not one to drink straight espresso — I prefer iced coffee all year round — but I really enjoyed the deep caramelly notes I achieved with the KF2. </p><p>When extracting an americano, I used the coffee setting with double strength, single cup dose, and it was pretty spot on for a black coffee. There was a bit of trial and error — initially, I opted for triple strength, and it was too strong for my liking — but the second time around, it worked a charm.</p><h2 id="milk">Milk</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5041px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="NkdudSkHwUV3XLebfPES69" name="kf2-10" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/NkdudSkHwUV3XLebfPES69.jpg" mos="" align="middle" fullscreen="" width="5041" height="2836" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>If you have milk with your coffee, then you’ll likely want to know how well the KF2 can froth milk. You may also have an opinion as to the best method for foaming milk. For me, the fact that the KF2 uses a conventional steam wand as opposed to a hose system on the KF3 and KF4 is a huge plus point. While it might not be as hands-off as the other KitchenAid models — you have to pour the milk into the jug (sold separately) and then pour it into your mug — the result, in my opinion, is much better milk foam. Conversely, when using the KF3 and KF4, milk was silky in texture and did have <em>some </em>froth, but it paled in comparison to what the KF2 could achieve.</p><p>What’s more, having manual control means the KF2 inherently performs better with plant-based milks. This is because you can easily check the temperature whilst milk is heating up and turn it off when it reaches the optimal heat. </p><p>I am a big fan of manual control when milk frothing, so I was very eager to try the steam wand, and as a plant-based milk drinker, I preferred the wand to the milk jug/hose set-up. I could easily check the temperature and make sure my almond milk didn't burn, as most plant-based milks will burn at lower temperatures than dairy. The wand also has a side toggle where you can adjust the foam to your liking, so you don't have to second-guess too much when heating milk to your preferred foaminess.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/3f3A4m6Z2Z67BeCCQSvsT8.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/4M7dzDwz26tbGhwieukBo3.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure></figure><p>Before using the steam wand, you need to wait a short while for the internal water system to preheat. It’s not a long process, just something to be aware of. When you’ve finished frothing your milk, you’ll want to purge the steam wand to remove any residue. The purge process can get quite messy as the wand is rather powerful and sits an inch or so above the drip tray. I resorted to purging it a few times inside an empty milk jug, just to avoid powerful steam explosions. </p><p>My first test was an Aussie staple, a flat white, which is typically made from steamed milk with very little foam. To do this, I flicked the foam setting to ‘less’ and put the wand in just below the surface. </p><p>The result clearly speaks for itself, as I made the most perfect flat white to ever exist. Even in my teenage barista days, I've never made glossy steamed milk like this, so it was a true feat to be admired for KitchenAid's steam wand. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/YdfX6AXKMmGte7U7P48FM7.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nwiPJLmAeRgSVnYvNffX49.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure></figure><p>Next up was a latte, so I moved the toggle to the highest foam setting and made sure to keep the wand under the surface of the milk. There were some bubbles initially, but after a few attempts, I ended up getting an equally perfect latte with some great microfoam. (My latte art definitely needs work, though.) </p><h2 id="iced-coffee">Iced coffee</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5521px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="f3xyqNW4kHKovQ8hAvFtf9" name="kf2-8" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/f3xyqNW4kHKovQ8hAvFtf9.jpg" mos="" align="middle" fullscreen="" width="5521" height="3106" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>KitchenAid has given its new compact trio of coffee machines the ability to make iced drinks, although the method in which they make them gave me a little cause for concern, as they’re made ‘over ice’. By this, it means you still need to put ice in your cup or glass first, and the KF2 will then pour a shot of espresso over it. The company says it brews at a cooler temperature than a ‘regular’ shot of espresso, but it’s not quite the same as the Ninja Luxe Cafe I use daily, for example, which has dedicated settings for brewing cold shots of espresso.</p><p>The results were interesting to say the least. I was a little anxious before making my first iced drink, because I'm picky about iced lattes, and if it's too watery, I don't want it. The fact that an iced latte made with the KF2 has to include a few ice cubes indicated that it may indeed end up being a bit weak. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5262px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AND2Hynrk7YcpkTjABJr79" name="kf2-6" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/AND2Hynrk7YcpkTjABJr79.jpg" mos="" align="middle" fullscreen="" width="5262" height="2960" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>Fortunately, my worries weren’t necessary, as the KF2 did a commendable job of making an iced coffee. When coupled with cold milk and ice, it was particularly delicious, although it was, admittedly, on the weaker side due to the melted ice. </p><p>After the first try, I tweaked the strength higher as the machine automatically selects the lowest setting, and it made a better improvement to the taste. I also opted most days to not brew it immediately over ice to avoid watery shots, and that made the experience much more pleasant. </p><h3 class="article-body__section" id="section-kitchenaid-kf2-review-storage-and-maintenance"><span>KitchenAid KF2 review: Storage and maintenance</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5280px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="SW6UYL2Wq3YWGMj28F8PS8" name="kf2-13" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/SW6UYL2Wq3YWGMj28F8PS8.jpg" mos="" align="middle" fullscreen="" width="5280" height="2970" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>When it comes to everyday maintenance of the KitchenAid KF2, it was pretty straightforward and I didn't need any handholding to keep the machine running. The drip tray has a handy full alert sign when it needs emptying, and can be easily removed. And if you need to add more coffee beans, the backlit screen will give you a helpful alert. </p><p>The KitchenAid KF2 comes with a couple of cleaning tablets for cleaning the brewing group and water filter inside the box. It also comes with a handy water filter insertion tool, which doubles as a ground coffee scoop. As per the manufacturer, the water filters will last two months if using tap water, but could last up to 6 months if you use filtered water. You will need to buy descaling solution separately.</p><p>KitchenAid's routine cleaning and maintenance is pretty easy, as the machine rinses itself before and after each use. This also clears the system of any blockages as well as warm the internal pipes to the right temperature before making a coffee.</p><p>If you need to descale the machine, clean the steam wand, add beans or water, the screen will give you an alert and guide you through the process if needed. Now, during my review period, I didn't need to descale the machine at all; however, I am certain that the machine would let me know if it needed an extra clean. </p><p>KitchenAid does sell additional water filters and brew units if you need more, and offers a 3-year limited warranty. </p><h3 class="article-body__section" id="section-kitchenaid-kf2-review-verdict"><span>KitchenAid KF2 review: Verdict</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5139px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="LEcu8EymDE63dPTETwCTi7" name="kf2-14" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/LEcu8EymDE63dPTETwCTi7.jpg" mos="" align="middle" fullscreen="" width="5139" height="2890" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>If you prioritise time in the mornings, then the KitchenAid KF2 is something to consider and add to your routine. It's easy-to-use, delivers excellent crema, and offers some hands-on control for your milk. All you need to do is add your preferred beans (or grounds), and you've got the perfect morning brew. </p><p>That said, it doesn't offer full one-touch convenience — you'll need to tweak your strength and dose settings with each use — however, that doesn't knock too many marks off for me. And iced lattes could be better, but that won't matter to most non-iced coffee drinkers. </p><p>That being said, if you're interested in a fully automatic, low-maintenance coffee machine, the KitchenAid KF2 is hard to beat. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/kitchenaid-kf2-review-this-fully-automatic-coffee-machine-effortlessly-takes-the-guesswork-out-of-your-morning-brew</link>
                                                                            <description>
                            <![CDATA[ KitchenAid's new fully automatic espresso machines are here — and I put its most affordable option to the test. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">vJ2VCQf53NUc84C3m4RxRD</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/brMt98Yp2XX2QMeZfKYHV8-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 03 Aug 2026 04:35:18 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ lucy.scotting@futurenet.com (Lucy Scotting) ]]></author>                    <dc:creator><![CDATA[ Lucy Scotting ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/UC9ncAYxkmJ5ipHEyX44ri.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Lucy Scotting is a staff writer for Tom’s Guide Australia, primarily covering lifestyle, streaming and internet-related news. Lucy started her career writing for HR and staffing industry publications, with articles covering emerging tech, business and finance.&lt;/p&gt;&lt;p&gt;In her spare time, Lucy can be found watching sci-fi movies, working on her dystopian fiction novel (in progress since 2017) or hanging out with her dog, Fletcher. If she’s not found doing any of the above, she’s likely on her next adventure to a new city, country, or continent. &lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/brMt98Yp2XX2QMeZfKYHV8-1280-80.jpg">
                                                            <media:credit><![CDATA[Future / Lucy Scotting]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[KitchenAid KF2 coffee machine in lifestyle setting]]></media:description>                                                            <media:text><![CDATA[KitchenAid KF2 coffee machine in lifestyle setting]]></media:text>
                                <media:title type="plain"><![CDATA[KitchenAid KF2 coffee machine in lifestyle setting]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/brMt98Yp2XX2QMeZfKYHV8-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As a kitchen appliance reviewer, some of my favorite and top-rated products hail from KitchenAid. I've owned an Artisan Stand Mixer for years now, and use many KitchenAid appliances and accessories on a daily basis, so it's no surprise that when the opportunity arose to review the brand's new fully automatic coffee machine range, I volunteered immediately. </p><p>Now, I'm no stranger to a manual espresso maker — I love my Ninja Luxe Cafe more than anything else — so I really wanted to switch it up to see how a fully automatic machine compares to the manual control I'm used to. After all, these automatic coffee machines claim to deliver the perfect espresso in one touch, without worrying about grinds, milk or the like, so my curiosity was definitely piqued. (And spoiler alert: the KF2 did not disappoint). </p><p>Launched earlier this year, KitchenAid's new machines — the KF2, KF3 and KF4 — each have different functions, prices and colorways to suit every kind of one-touch coffee-lover. The machine reviewed here, the KitchenAid KF2, is the most affordable option and offers some hands-on time for hobbyist baristas who want manual control over their milk. </p><p>Over the course of a month, I put the KitchenAid KF2 through its paces and used it to make hot, iced and milk-based drinks.</p><h3 class="article-body__section" id="section-kitchenaid-kf2-review-specs"><span>KitchenAid KF2 review: Specs</span></h3><div ><table><caption>KitchenAid KF2 specs</caption><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p>$799.99 / £699 / AU$1,099</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>22.2lbs / 10.1kg</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>14.2"H × 7.7"W × 18.5"D / 36.07cm H × 19.56cm W × 46.99cm D</p></td></tr><tr><td class="firstcol " ><p><strong>Drink options</strong></p></td><td  ><p>6 – including espresso, coffee and over ice</p></td></tr><tr><td class="firstcol " ><p><strong>Grind settings</strong></p></td><td  ><p>5</p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>250g bean hopper & 250g ground coffee</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank</strong></p></td><td  ><p>1.8L</p></td></tr><tr><td class="firstcol " ><p><strong>Material</strong></p></td><td  ><p>Stainless steel</p></td></tr></tbody></table></div><h3 class="article-body__section" id="section-kitchenaid-kf2-review-price-and-availability"><span>KitchenAid KF2 review: Price and availability</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5492px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5kgtd9R3rJx4jo3k8ZHpK9" name="kf2-17" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/5kgtd9R3rJx4jo3k8ZHpK9.jpg" mos="" align="middle" fullscreen="" width="5492" height="3089" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>The KitchenAid KF2 in Australia, where I'm based, is priced at AU$1,099. For those in the UK, it's £699 and in the U.S., it's $799.99. When compared to the other coinciding releases of the KF3 ($999.99 / £749 / AU$1,349) and KF4 ($1,299.99 / £899 / AU$1,599), this is the "budget" or entry-level option in KitchenAid's new fully automatic lineup. </p><p>Now the word budget is a bit of a stretch — it's still pretty expensive — and it doesn't quite dazzle compared to similarly priced automatic machines. I’m talking largely about De’Longhi here, as you can often pick up one of the esteemed brand’s machines for less, in return for more extensive features, like one-touch drink presets and automatic milk-foaming systems. That said, the KF2 offers six drink presets and a steam wand (which some <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers#section-best-superautomatic">5-star automatic machines</a> neglect entirely). And, I won't lie, the KF2's sleek, matte black design is nice to look at. </p><h3 class="article-body__section" id="section-kitchenaid-kf2-review-design"><span>KitchenAid KF2 review: Design</span></h3><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/B42ouYAGfY9UcZkv8FjnA9.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/qf4jvodr7TjtuFH6MYEgi3.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/7DBniEP8J5Udvh5mDMLRa8.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/HMQ7WNqCQYUNbNY54LWtc7.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MoKzrMtAcqDguKeB6dAZQ8.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure></figure><p>KitchenAid's newest fully automatic machines, including the KF2, have a much smaller footprint than their predecessors. Measuring just 14.2" x 7.7" x 18.5" / 36.07cm x 19.56cm x 46.99cm, the KF2 takes up very little counterspace, making it ideal for those with smaller kitchens. The KF2 has five function buttons on the front of its backlit LED screen, as well as a power button on the right side. It also features a 1.8L water tank at the back, a Pannarello steam wand, a drip tray and a concealed 250g bean hopper. </p><p>Unlike the KF3 and KF4, which are both available in matte black and KitchenAid's signature Porcelain white, the KF2 is limited to the former. I quite like the simplicity of the black colorway, but it isn't as visually appealing as the white or Juniper green colorways that the KitchenAid KF8 offers, in my opinion. The stainless steel accents in the drip tray and the steam wand are a nice contrast to the matte machine body, but I didn't love the feel of the function buttons.</p><p>A fun surprise discovered during set up were two wheels under the machine, making it easy to move around if you needed to fill the water tank. It's also not that heavy, at just 22lbs / 10.1kg, so I could easily lift it and move it into place.</p><h3 class="article-body__section" id="section-kitchenaid-kf2-review-performance"><span>KitchenAid KF2 review: Performance</span></h3><h2 id="grinder">Grinder</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5399px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="z4MCQPoBhigQt97L7BzV98" name="kf2-19" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/z4MCQPoBhigQt97L7BzV98.jpg" mos="" align="middle" fullscreen="" width="5399" height="3037" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>The KF2 uses KitchenAid’s Intelligrind technology, which aims to take the guesswork out of selecting the best grind size for your chosen beans. For most users, it allows for a hands-off approach, just pour your beans into the top-mounted hopper and select a drink. If you do want to make some finer adjustments, you can open the machine's side panel, where you’ll find a slider to make the grind finer (to the left) or coarser (to the right). However, for most users, I don't think any adjustments would need to be made, as the automatic function takes away any manual hand tamping or grinding. </p><p>Regardless of whether you make adjustments or not, the KF2's grinder is so quiet. That's largely due to its Quiet Mark certification, meaning it won't make too much noise while you're brewing your early morning coffee. </p><p>If you prefer decaf or coffee grounds, the KF2 also has a 250g ground coffee section next to the bean hopper. It is worth noting that when you add in ground coffee, the machine will switch into its ground coffee mode and exclusively use it over any beans in the hopper. To fix this, you'll need to toggle the bean setting before every use. </p><h2 id="espresso">Espresso</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3815px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TWEoQeT5x7nJbUAns53bL5" name="kf2-9" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/TWEoQeT5x7nJbUAns53bL5.jpg" mos="" align="middle" fullscreen="" width="3815" height="2146" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>The KF2 can truly customise your coffee to your liking, thanks to its drink, strength and dosage settings. Once you choose your drink recipe — e.g. either an espresso, coffee or over ice — you can make your coffee or espresso shot as weak or strong as you'd like using the strength and dose buttons. If you make adjustments to the strength, the KF2 will grind fewer or more beans, resulting in a dose that’s between 7g and 14g.</p><p>Regardless of the strength setting you choose, the KF2 excels at pulling a consistently well-extracted shot of espresso. For the test brew, I used the double strength on the espresso setting to extract a single shot, and it was perfectly balanced. Just look at that crema! This was using medium-dark roast beans with notes of rich caramel, butterscotch and sweet red fruits. I'm not one to drink straight espresso — I prefer iced coffee all year round — but I really enjoyed the deep caramelly notes I achieved with the KF2. </p><p>When extracting an americano, I used the coffee setting with double strength, single cup dose, and it was pretty spot on for a black coffee. There was a bit of trial and error — initially, I opted for triple strength, and it was too strong for my liking — but the second time around, it worked a charm.</p><h2 id="milk">Milk</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5041px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="NkdudSkHwUV3XLebfPES69" name="kf2-10" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/NkdudSkHwUV3XLebfPES69.jpg" mos="" align="middle" fullscreen="" width="5041" height="2836" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>If you have milk with your coffee, then you’ll likely want to know how well the KF2 can froth milk. You may also have an opinion as to the best method for foaming milk. For me, the fact that the KF2 uses a conventional steam wand as opposed to a hose system on the KF3 and KF4 is a huge plus point. While it might not be as hands-off as the other KitchenAid models — you have to pour the milk into the jug (sold separately) and then pour it into your mug — the result, in my opinion, is much better milk foam. Conversely, when using the KF3 and KF4, milk was silky in texture and did have <em>some </em>froth, but it paled in comparison to what the KF2 could achieve.</p><p>What’s more, having manual control means the KF2 inherently performs better with plant-based milks. This is because you can easily check the temperature whilst milk is heating up and turn it off when it reaches the optimal heat. </p><p>I am a big fan of manual control when milk frothing, so I was very eager to try the steam wand, and as a plant-based milk drinker, I preferred the wand to the milk jug/hose set-up. I could easily check the temperature and make sure my almond milk didn't burn, as most plant-based milks will burn at lower temperatures than dairy. The wand also has a side toggle where you can adjust the foam to your liking, so you don't have to second-guess too much when heating milk to your preferred foaminess.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/3f3A4m6Z2Z67BeCCQSvsT8.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/4M7dzDwz26tbGhwieukBo3.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure></figure><p>Before using the steam wand, you need to wait a short while for the internal water system to preheat. It’s not a long process, just something to be aware of. When you’ve finished frothing your milk, you’ll want to purge the steam wand to remove any residue. The purge process can get quite messy as the wand is rather powerful and sits an inch or so above the drip tray. I resorted to purging it a few times inside an empty milk jug, just to avoid powerful steam explosions. </p><p>My first test was an Aussie staple, a flat white, which is typically made from steamed milk with very little foam. To do this, I flicked the foam setting to ‘less’ and put the wand in just below the surface. </p><p>The result clearly speaks for itself, as I made the most perfect flat white to ever exist. Even in my teenage barista days, I've never made glossy steamed milk like this, so it was a true feat to be admired for KitchenAid's steam wand. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/YdfX6AXKMmGte7U7P48FM7.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nwiPJLmAeRgSVnYvNffX49.jpg" alt="KitchenAid KF2 coffee machine in lifestyle setting" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure></figure><p>Next up was a latte, so I moved the toggle to the highest foam setting and made sure to keep the wand under the surface of the milk. There were some bubbles initially, but after a few attempts, I ended up getting an equally perfect latte with some great microfoam. (My latte art definitely needs work, though.) </p><h2 id="iced-coffee">Iced coffee</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5521px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="f3xyqNW4kHKovQ8hAvFtf9" name="kf2-8" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/f3xyqNW4kHKovQ8hAvFtf9.jpg" mos="" align="middle" fullscreen="" width="5521" height="3106" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>KitchenAid has given its new compact trio of coffee machines the ability to make iced drinks, although the method in which they make them gave me a little cause for concern, as they’re made ‘over ice’. By this, it means you still need to put ice in your cup or glass first, and the KF2 will then pour a shot of espresso over it. The company says it brews at a cooler temperature than a ‘regular’ shot of espresso, but it’s not quite the same as the Ninja Luxe Cafe I use daily, for example, which has dedicated settings for brewing cold shots of espresso.</p><p>The results were interesting to say the least. I was a little anxious before making my first iced drink, because I'm picky about iced lattes, and if it's too watery, I don't want it. The fact that an iced latte made with the KF2 has to include a few ice cubes indicated that it may indeed end up being a bit weak. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5262px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AND2Hynrk7YcpkTjABJr79" name="kf2-6" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/AND2Hynrk7YcpkTjABJr79.jpg" mos="" align="middle" fullscreen="" width="5262" height="2960" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>Fortunately, my worries weren’t necessary, as the KF2 did a commendable job of making an iced coffee. When coupled with cold milk and ice, it was particularly delicious, although it was, admittedly, on the weaker side due to the melted ice. </p><p>After the first try, I tweaked the strength higher as the machine automatically selects the lowest setting, and it made a better improvement to the taste. I also opted most days to not brew it immediately over ice to avoid watery shots, and that made the experience much more pleasant. </p><h3 class="article-body__section" id="section-kitchenaid-kf2-review-storage-and-maintenance"><span>KitchenAid KF2 review: Storage and maintenance</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5280px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="SW6UYL2Wq3YWGMj28F8PS8" name="kf2-13" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/SW6UYL2Wq3YWGMj28F8PS8.jpg" mos="" align="middle" fullscreen="" width="5280" height="2970" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>When it comes to everyday maintenance of the KitchenAid KF2, it was pretty straightforward and I didn't need any handholding to keep the machine running. The drip tray has a handy full alert sign when it needs emptying, and can be easily removed. And if you need to add more coffee beans, the backlit screen will give you a helpful alert. </p><p>The KitchenAid KF2 comes with a couple of cleaning tablets for cleaning the brewing group and water filter inside the box. It also comes with a handy water filter insertion tool, which doubles as a ground coffee scoop. As per the manufacturer, the water filters will last two months if using tap water, but could last up to 6 months if you use filtered water. You will need to buy descaling solution separately.</p><p>KitchenAid's routine cleaning and maintenance is pretty easy, as the machine rinses itself before and after each use. This also clears the system of any blockages as well as warm the internal pipes to the right temperature before making a coffee.</p><p>If you need to descale the machine, clean the steam wand, add beans or water, the screen will give you an alert and guide you through the process if needed. Now, during my review period, I didn't need to descale the machine at all; however, I am certain that the machine would let me know if it needed an extra clean. </p><p>KitchenAid does sell additional water filters and brew units if you need more, and offers a 3-year limited warranty. </p><h3 class="article-body__section" id="section-kitchenaid-kf2-review-verdict"><span>KitchenAid KF2 review: Verdict</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5139px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="LEcu8EymDE63dPTETwCTi7" name="kf2-14" alt="KitchenAid KF2 coffee machine in lifestyle setting" src="https://cdn.mos.cms.futurecdn.net/LEcu8EymDE63dPTETwCTi7.jpg" mos="" align="middle" fullscreen="" width="5139" height="2890" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>If you prioritise time in the mornings, then the KitchenAid KF2 is something to consider and add to your routine. It's easy-to-use, delivers excellent crema, and offers some hands-on control for your milk. All you need to do is add your preferred beans (or grounds), and you've got the perfect morning brew. </p><p>That said, it doesn't offer full one-touch convenience — you'll need to tweak your strength and dose settings with each use — however, that doesn't knock too many marks off for me. And iced lattes could be better, but that won't matter to most non-iced coffee drinkers. </p><p>That being said, if you're interested in a fully automatic, low-maintenance coffee machine, the KitchenAid KF2 is hard to beat. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I was spending $7 a day at coffee shops — now I spend $1 thanks to my Nespresso machine that’s 41% off at Amazon ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I love going to coffee shops, whether I'm just quickly grabbing my favorite vanilla oat milk latte, meeting up with a friend for coffee or getting some work done. However, every time I step foot inside a cafe, I'm spending at least $7 — and that's just for a coffee. </p><p>After calculating how much I was spending weekly and monthly, I was astonished. That's when I decided I needed to cut back a bit — and <a href="https://www.amazon.com/Nespresso-DeLonghi-ENV120GYAE-Espresso-Aeroccino/dp/B084GY5FHN/" target="_blank" rel="nofollow">my Nespresso Vertuo Next coffee and espresso maker</a> made that possible. Even better? It's currently 41% off on Amazon. </p><div class="product"><a data-dimension112="85336e50-8cea-11f1-9071-8d73434d97ba" data-action="Deal Block" data-label="Vertuo Next w Aeroccino Milk Frother" data-dimension48="Vertuo Next w Aeroccino Milk Frother" data-dimension25="$134" href="https://www.amazon.com/Nespresso-DeLonghi-ENV120GYAE-Espresso-Aeroccino/dp/B084GY5FHN/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:84.93%;"><img id="cqkR72BCgMgQiYg32kT4jW" name="Nespresso Vertuo" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/cqkR72BCgMgQiYg32kT4jW.jpg" mos="" align="middle" fullscreen="" width="1500" height="1274" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Nespresso-DeLonghi-ENV120GYAE-Espresso-Aeroccino/dp/B084GY5FHN/" target="_blank" rel="nofollow" data-dimension112="85336e50-8cea-11f1-9071-8d73434d97ba" data-action="Deal Block" data-label="Vertuo Next w Aeroccino Milk Frother" data-dimension48="Vertuo Next w Aeroccino Milk Frother" data-dimension25="$134">Vertuo Next w Aeroccino Milk Frother: was $229 now $134</a></strong><br>This Nespresso Vertuo Next machine can turn your home into a full on coffee shop. It supports six cup sizes including 5, 8, 12, and 18-oz., as well as single and double espressos brewed hot or over ice. The included Aeroccino frother provides hot or cold milk for your cappuccinos and lattes.<a class="view-deal button" href="https://www.amazon.com/Nespresso-DeLonghi-ENV120GYAE-Espresso-Aeroccino/dp/B084GY5FHN/" target="_blank" rel="nofollow" data-dimension112="85336e50-8cea-11f1-9071-8d73434d97ba" data-action="Deal Block" data-label="Vertuo Next w Aeroccino Milk Frother" data-dimension48="Vertuo Next w Aeroccino Milk Frother" data-dimension25="$134">View Deal</a></p></div><div class="product"><a data-dimension112="85336ef0-8cea-11f1-81ca-d9115b0a4f10" data-action="Deal Block" data-label="Capsules Vertuo" data-dimension48="Capsules Vertuo" data-dimension25="$42" href="https://www.amazon.com/Nespresso-Vertuoline-Seller-Assortment-Count/dp/B01N05APQY/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="Yvm4K4QWtX9JaZ3avkGG6L" name="Nespresso capsules" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Yvm4K4QWtX9JaZ3avkGG6L.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Nespresso-Vertuoline-Seller-Assortment-Count/dp/B01N05APQY/" target="_blank" rel="nofollow" data-dimension112="85336ef0-8cea-11f1-81ca-d9115b0a4f10" data-action="Deal Block" data-label="Capsules Vertuo" data-dimension48="Capsules Vertuo" data-dimension25="$42">Capsules Vertuo: $42</a></strong><br>These are the Nespresso capsules I buy on a monthly basis. The variety pack includes three different medium and dark roast coffee pods with rich flavors you'll love. They'll provide you with a 7.8 oz serving of coffee. <a class="view-deal button" href="https://www.amazon.com/Nespresso-Vertuoline-Seller-Assortment-Count/dp/B01N05APQY/" target="_blank" rel="nofollow" data-dimension112="85336ef0-8cea-11f1-81ca-d9115b0a4f10" data-action="Deal Block" data-label="Capsules Vertuo" data-dimension48="Capsules Vertuo" data-dimension25="$42">View Deal</a></p></div><p>At nearly $100 off, the Nespresso Vertuo Next is practically a steal — and it can quickly pay for itself compared to buying coffee from a café every day. I typically buy Nespresso Vertuo capsules, which cost $42 for a pack of 30. That works out to about $1.40 per cup, making it a much more affordable daily coffee habit than my typical coffee shop run.</p><p>The best part is, my Nespresso machine actually makes delicious coffees, lattes and cappuccinos, so I don't feel disappointed making coffee at home rather than buying one. The included Aeroccino Milk Frother is really the icing on the cake. I add my favorite oat milk to the frother and in seconds, I have creamy, frothy milk to add to my coffee or latte. </p><p>The machine is super easy to use — all you have to do is add your capsule, make sure the water tank is full and press the brewing button. You'll have a delicious, barista-style coffee in just seconds. You can also choose from six cup sizes including 5, 8, 12, and 18-oz., as well as single and double espressos brewed hot or over ice. The machine truly does it all! </p><p>Of course, I'll never fully stop indulging in my local coffee shops — but at least I can save a little bit of money by making my coffee at home a few days a week. If you're hoping to do the same, <a href="https://www.amazon.com/Nespresso-DeLonghi-ENV120GYAE-Espresso-Aeroccino/dp/B084GY5FHN/" target="_blank" rel="nofollow">I recommend grabbing the Nespresso Vertuo Next for just $134 on Amazon</a>. </p><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=coffee&view_mode=savings_squad&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function getTimeAgo(dateString) {        if (!dateString) return '';        const date = new Date(dateString);        const now = new Date();        const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);        if (diffInSeconds < 60) return 'Just now';        const diffInMinutes = Math.floor(diffInSeconds / 60);        if (diffInMinutes < 60) return `${diffInMinutes} min${diffInMinutes > 1 ? 's' : ''} ago`;        const diffInHours = Math.floor(diffInMinutes / 60);        if (diffInHours < 24) return `${diffInHours} hr${diffInHours > 1 ? 's' : ''} ago`;        const diffInDays = Math.floor(diffInHours / 24);        if (diffInDays < 30) return `${diffInDays} day${diffInDays > 1 ? 's' : ''} ago`;        const diffInMonths = Math.floor(diffInDays / 30);        if (diffInMonths < 12) return `${diffInMonths} mo${diffInMonths > 1 ? 's' : ''} ago`;        const diffInYears = Math.floor(diffInDays / 365);        return `${diffInYears} yr${diffInYears > 1 ? 's' : ''} ago`;      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;      position: relative;      z-index: 20;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      margin: 0 auto;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters-container {      position: relative;      width: 100%;      max-width: 800px;    }    .tg-df-scroll-btn {      display: none;      position: absolute;      top: 50%;      transform: translateY(-50%);      width: 32px;      height: 32px;      background: white;      border: 1px solid var(--tg-df-border);      border-radius: 50%;      align-items: center;      justify-content: center;      cursor: pointer;      z-index: 10;      box-shadow: 0 2px 8px rgba(0,0,0,0.1);      color: var(--tg-df-text-primary);      padding: 0;    }    .tg-df-scroll-btn svg {      width: 16px;      height: 16px;    }    .tg-df-scroll-btn:hover {      background: #f4f4f4;    }    .tg-df-scroll-btn.left {      left: 0px;    }    .tg-df-scroll-btn.right {      right: 0px;    }    @container tg-df (max-width: 768px) {      .tg-df-scroll-btn {        display: flex;        top: 22px; /* vertically center within the 44px high filter buttons */      }    }    .tg-df-filters {      display: grid;      width: 100%;      grid-template-columns: repeat(4, 1fr);      gap: 12px;      margin: 0 auto;      max-width: 800px;    }                 .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;      width: 100%;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      width: 100%;      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }    @container tg-df (max-width: 768px) {      .tg-df-filters-container {      position: relative;      width: 100%;      max-width: 800px;    }    .tg-df-scroll-btn {      display: none;      position: absolute;      top: 50%;      transform: translateY(-50%);      width: 32px;      height: 32px;      background: white;      border: 1px solid var(--tg-df-border);      border-radius: 50%;      align-items: center;      justify-content: center;      cursor: pointer;      z-index: 10;      box-shadow: 0 2px 8px rgba(0,0,0,0.1);      color: var(--tg-df-text-primary);      padding: 0;    }    .tg-df-scroll-btn svg {      width: 16px;      height: 16px;    }    .tg-df-scroll-btn:hover {      background: #f4f4f4;    }    .tg-df-scroll-btn.left {      left: 0px;    }    .tg-df-scroll-btn.right {      right: 0px;    }    @container tg-df (max-width: 768px) {      .tg-df-scroll-btn {        display: flex;        top: 22px; /* vertically center within the 44px high filter buttons */      }    }    .tg-df-filters {        width: 100%;        margin: 0;        margin-bottom: -320px;        padding: 0 16px 320px 16px;        display: flex;        flex-wrap: nowrap;        gap: 8px;        overflow-x: auto;        overflow-y: hidden;        pointer-events: none;        scrollbar-width: none;        -webkit-overflow-scrolling: touch;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }      .tg-df-sort-wrapper {        pointer-events: auto;        flex: 0 0 auto;        width: 175px;        min-width: 175px;      }    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 120px;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 16px 16px 8px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }                  .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;        width: auto;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;      width: 100vw;      max-width: 1200px;      position: relative;      left: 50%;      transform: translateX(-50%);    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 14px;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    .tg-df-grid-wrapper { position: relative; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn {      display: inline-flex;      align-items: center;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.40);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #1f69ff; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #0056e0; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }    .tg-df-savings-squad-header { margin-bottom: 24px; text-align: center; display: none; }    .tg-df-banner-img-desktop { display: block; width: 100%; height: auto; margin-bottom: 32px; }    .tg-df-banner-img-mobile { display: none; width: 100%; height: auto; margin-bottom: 32px; }    @container tg-df (max-width: 600px) {      .tg-df-banner-img-desktop { display: none; }      .tg-df-banner-img-mobile { display: block; }    }    .tg-df-header-title { font-size: 28px; font-weight: 700; color: var(--tg-df-text); margin: 32px 0 12px 0; line-height: 1.3; }    .tg-df-header-subtitle { font-size: 16px; color: var(--tg-df-text-muted); margin: 0 0 32px 0; line-height: 1.5; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3Cdiv class="tg-df-savings-squad-header" id="tg-df-savings-squad-header">      \x3Cpicture>        \x3Cimg src="https://cdn.mos.cms.futurecdn.net/flexiimages/xkh2og7m3d1778189998.png" alt="Deals Banner" class="tg-df-banner-img-desktop" />        \x3Cimg src="https://cdn.mos.cms.futurecdn.net/flexiimages/gmak6rtdf41778245089.png" alt="Deals Banner Mobile" class="tg-df-banner-img-mobile" />      \x3C/picture>      \x3Cdiv class="tg-df-header-text">        \x3Ch2 class="tg-df-header-title" id="tg-df-header-title">Editor's Choice Deals\x3C/h2>        \x3Cp class="tg-df-header-subtitle" id="tg-df-header-subtitle">Discover the best discounts currently available, curated daily by the Tom's Guide Savings Squad.\x3C/p>      \x3C/div>    \x3C/div>    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Televisions" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/wcMxTsHgqu3roMbAx7RLnT-132-100.png" alt="TVs" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">TVs\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Phones" data-pr="over50">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/G3KGaRGzj24F6PUsw4bWpT-132-100.png" alt="Phones" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Phones\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Computing" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/znNvsLzx8NEgNkD9HSFSnT-132-100.png" alt="Computing" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Computing\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Gaming" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/Pgew8yaRQeZFHqHjTzvBnT-132-100.png" alt="Gaming" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Gaming\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Mattresses" data-pr="over500">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/cW7xsaLyesxkHFVSiC4kmT-132-100.png" alt="Mattresses" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Mattresses\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Audio" data-pr="over30">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/pCvBVHuhaQVjKt3VgCjbqT-132-100.png" alt="Audio" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Audio\x3C/span>            \x3C/div>                  \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E      \x3Cdiv class="tg-df-top-bar" id="tg-df-top-bar" style="position: relative; z-index: 100; margin: 0 auto 20px;">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>      \x3C/div>    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">              \x3Cdiv class="tg-df-filters-container" style="position: relative; width: 100%; max-width: 800px; margin: 0 auto;">          \x3Cbutton class="tg-df-scroll-btn left" style="display: none;" onclick="this.parentElement.querySelector('.tg-df-filters').scrollBy({left: -200, behavior: 'smooth'})">            \x3Csvg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\x3Cpath d="M15 18l-6-6 6-6"/>\x3C/svg>          \x3C/button>          \x3Cbutton class="tg-df-scroll-btn right" style="display: none;" onclick="this.parentElement.querySelector('.tg-df-filters').scrollBy({left: 200, behavior: 'smooth'})">            \x3Csvg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\x3Cpath d="M9 18l6-6-6-6"/>\x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-filters">          \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>          \x3C/div>        \x3C/div>        \x3C/div>      \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();        }        getViewMode() {          console.log("DEBUG getViewMode -> override:", this.viewModeOverride, "editorMode:", this.editorMode);          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          console.log("[DEBUG] applyLayoutMode CALLED! mode=", mode);          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          const showHeaderDetails = params.get('show_header_details') !== 'false';          const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          const topBarDiv = this.root.querySelector('#tg-df-top-bar');          const headerElement = this.root.querySelector('#tg-df-savings-squad-header');          if (headerElement) {             headerElement.style.display = (mode === 'savings_squad' && showHeaderDetails) ? 'block' : 'none';          }          if (mode === 'carousel') {             this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (topBarDiv) topBarDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.add('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (topBarDiv) topBarDiv.style.display = 'block';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!this.viewModeOverride && initialViewMode) {            this.viewModeOverride = initialViewMode;          }          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Gaming laptops';          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (this.viewModeSelect && params.has('view_mode')) {            this.viewModeSelect.value = params.get('view_mode');          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'date_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }          const headerTitleEl = this.root.querySelector('#tg-df-header-title');          const headerSubtitleEl = this.root.querySelector('#tg-df-header-subtitle');          if (params.has('widget_title') && headerTitleEl) {             headerTitleEl.textContent = params.get('widget_title');          }          if (params.has('widget_subtitle') && headerSubtitleEl) {             headerSubtitleEl.textContent = params.get('widget_subtitle');          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            if (this.getViewMode() === 'savings_squad') {              this.searchInput.value = '';            } else {              this.searchInput.value = this.currentQuery;            }          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'date_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const handleFiltersScroll = () => {            const filters = this.root.querySelector('.tg-df-filters');            const leftBtn = this.root.querySelector('.tg-df-scroll-btn.left');            const rightBtn = this.root.querySelector('.tg-df-scroll-btn.right');            if (filters && leftBtn && rightBtn) {              const { scrollLeft, scrollWidth, clientWidth } = filters;              leftBtn.style.display = scrollLeft > 0 ? 'flex' : 'none';              rightBtn.style.display = Math.ceil(scrollLeft + clientWidth) < scrollWidth - 5 ? 'flex' : 'none';            }          };          const filters = this.root.querySelector('.tg-df-filters');          if (filters) {            filters.addEventListener('scroll', handleFiltersScroll);            window.addEventListener('resize', handleFiltersScroll);            setTimeout(handleFiltersScroll, 100);                        // Also call after rendering dropdowns            const origRenderCategories = this.renderCategories;            if (origRenderCategories) {               this.renderCategories = (...args) => {                 origRenderCategories.apply(this, args);                 setTimeout(handleFiltersScroll, 50);               };            }          }                const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          const showAutocomplete = () => {             if (this.getViewMode() !== 'savings_squad' || !this.autocompleteDropdown || !this.airedaleTags) return;                          let terms = this.airedaleTags;             if (this.airedaleBrands) {                terms = terms.concat(this.airedaleBrands.map(b => b.formatted_value));             }             terms = [...new Set(terms)];                          const query = this.searchInput.value.trim();             let matches = [];             if (query.length > 0) {                 matches = terms.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase());             } else {                 matches = terms;             }                          if (matches.length > 0) {                 this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                 this.autocompleteDropdown.classList.add('active');             } else {                 this.autocompleteDropdown.classList.remove('active');             }          };          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('focus', showAutocomplete);            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              showAutocomplete();              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  if (this.getViewMode() === 'savings_squad') {                    this.activeDealTag = null;                    this.currentQuery = '';                    if (this.categoryFilter) this.categoryFilter.value = 'all';                    this.fetchDeals('');                  } else {                    this.deals = [];                    this.render();                  }                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                                let isTag = false;                if (this.airedaleTags && this.airedaleTags.includes(query)) isTag = true;                if (this.airedaleBrands && this.airedaleBrands.some(b => b.formatted_value === query)) isTag = true;                this.activeDealTag = isTag ? query : null;                                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   if (query.length === 0 && this.getViewMode() === 'savings_squad') {                       if (this.categoryFilter) this.categoryFilter.value = 'all';                   }                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   if (this.categoryFilter && this.airedaleTags.includes(tag)) {                       this.categoryFilter.value = tag;                   }                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                            let isTag = false;              if (this.airedaleTags && this.airedaleTags.includes(query)) isTag = true;              if (this.airedaleBrands && this.airedaleBrands.some(b => b.formatted_value === query)) isTag = true;              this.activeDealTag = isTag ? query : null;                            this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 if (query.length === 0 && this.getViewMode() === 'savings_squad') {                     if (this.categoryFilter) this.categoryFilter.value = 'all';                 }                 this.fetchDeals(query);              }            });          }          if(this.sortSelect && this.sortSelect.querySelector('option[value="date_desc"]') === null) {              const option = document.createElement('option');              option.value = "date_desc";              option.text = "Newest First";              this.sortSelect.insertBefore(option, this.sortSelect.firstChild);          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               if (val) {                 this.currentQuery = val;               } else {                 if (this.searchInput && this.currentQuery === document.querySelector('#tg-df-brand-trigger')?.getAttribute('data-active-brand')) {                     // don't clear current query if a brand is selected                 } else if (this.searchInput) {                     this.currentQuery = '';                     this.searchInput.value = '';                 }               }               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = 'date_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }        async fetchDeals(query) {          this.showLoading();          this.deals = [];          this.displayLimit = this.getRowsLimit();                    try {            console.log("getViewMode returns:", this.getViewMode());            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad();            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query);               } else {                 await this.fetchHawkDeals(query);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const siteParam = this.website || 'tomsguide';            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1781000519267.json?site=${siteParam}&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=${siteParam}&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (!this.activeDealTag && this.currentQuery) {             const tagMatch = this.airedaleTags.find(t => t.toLowerCase() === this.currentQuery.toLowerCase());             if (tagMatch) {                this.activeDealTag = tagMatch;             }          }          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const siteParam = this.website || 'tomsguide';             const url = `https://airedale.futurecdn.net/feeds/feed_1781000519267.json?site=${siteParam}&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let seenUrls = new Set();                    let overallBrandsCounts = {};                    // First pass: extract ALL brands from topArticles so the dropdown has all options          topArticles.forEach((article) => {             if (!article.articlepage) return;             let pageData = [];             try { pageData = JSON.parse(article.articlepage[0]); } catch(e){ console.warn(e); }             const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');             savingsSquad.forEach((block) => {                const data = block.data || {};                if (data.brand) {                   const cleanBrand = data.brand.replace(/^\d+\.\s*/, '').trim();                   overallBrandsCounts[cleanBrand] = (overallBrandsCounts[cleanBrand] || 0) + 1;                }             });          });          targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                if (externalUrl) {                   if (seenUrls.has(externalUrl)) return;                  seenUrls.add(externalUrl);                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || '',                   authorName: article.articleauthortext ? article.articleauthortext[0] : (article.articleauthor ? article.articleauthor[0] : ''),                   authorRole: article.articleauthorrole ? article.articleauthorrole[0] : '',                   authorImage: article.articleauthormedia ? article.articleauthormedia[0] : '',                   documentUrl: article.documenturl ? article.documenturl[0] : '',                   modifiedDate: article.contentmodifieddate || article.modifieddate || ''                });             });          });                    const airedaleBrandsList = Object.keys(overallBrandsCounts).map(b => ({              formatted_value: b,              count: overallBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : 'date_desc';          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                ${_div}              ${_div}              \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin:0; border-radius:0;">${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)}${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = 'View Deal';              if (isSavingsSquadMode) {                priceGroupHtml = ``;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = isSavingsSquadMode ? `` : `                \x3Cdiv class="tg-df-card-price-group">                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description tg-df-card-desc-container" style="margin-bottom: 12px; position: relative;">                          \x3Cp class="tg-df-card-desc-content" style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 0; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;">${this.escapeHTML(deal.description)}${_p}                          \x3Cbutton type="button" class="tg-df-card-desc-btn" style="display: none; appearance: none; border: none; color: #000000; font-size: 11px; font-weight: 700; text-transform: uppercase; cursor: pointer; font-family: inherit; position: absolute; bottom: 2px; right: 0; background: linear-gradient(to right, transparent, #fff 20%, #fff); padding: 0 0 0 16px;" onclick="                            var c = this.parentNode;                            if (this.dataset.expanded === 'true') {                              var pd = (c.tagName === 'P') ? c : this.previousElementSibling;                              if (c.tagName === 'P') { c.parentNode.appendChild(this); pd = c; }                              pd.style.display = '-webkit-box';                              pd.style.webkitLineClamp = '3';                              this.textContent = 'READ MORE';                              this.style.position = 'absolute';                              this.style.background = 'linear-gradient(to right, transparent, #fff 20%, #fff)';                              this.style.paddingLeft = '16px';                              this.dataset.expanded = 'false';                            } else {                              var pd = this.previousElementSibling;                              pd.style.display = 'inline';                              pd.style.webkitLineClamp = 'unset';                              this.textContent = 'READ LESS';                              this.style.position = 'static';                              this.style.background = 'transparent';                              this.style.paddingLeft = '4px';                              this.dataset.expanded = 'true';                              pd.appendChild(this);                            }                          ">READ MORE${_button}                        \x3C/div>` : ''}                      ${_div}                    ${_div}                    ${deal.authorName ? `                      \x3Cdiv class="tg-df-author-line-mobile" style="padding: 0 0 12px 0; background: transparent;">                         \x3Cdiv style="display: flex; align-items: center; gap: 12px;">                            ${deal.authorImage ? `\x3Cimg src="${this.escapeHTML(deal.authorImage)}" alt="${this.escapeHTML(deal.authorName)}" class="tg-df-author-img" width="40" height="40" style="border-radius: 50%; object-fit: cover; flex-shrink: 0; aspect-ratio: 1 / 1; min-width: 40px; min-height: 40px;">` : ''}                            \x3Cdiv style="display: flex; flex-direction: column;">                               \x3Cdiv style="font-size: 10px; color: var(--tg-df-text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px; font-weight: 600;">\x3Cspan style="color: #FF6600;">${this.escapeHTML(deal.merchant)}${_span} deal recommended by:${_div}                               \x3Cdiv style="font-size: 11px; color: var(--tg-df-text); line-height: 1.3;">                                  \x3Cstrong>\x3Ca href="https://www.tomsguide.com/${this.escapeHTML(deal.documentUrl || '').replace(/^\/+/, '')}" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit; border-bottom: 1px dotted var(--tg-df-text-muted);">${this.escapeHTML(deal.authorName)}${_a}${_strong}                                  ${deal.authorRole && !['null', 'nul', 'undefined'].includes(String(deal.authorRole).toLowerCase()) ? ` • ${this.escapeHTML(deal.authorRole)}` : ''}                                  ${deal.modifiedDate ? `\x3Cdiv style="color: var(--tg-df-text-muted); margin-top: 2px;">${getTimeAgo(deal.modifiedDate)}${_div}` : ''}                               ${_div}                            ${_div}                         ${_div}                      ${_div}                    ` : ''}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>View Deal${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                  \x3Cdiv class="tg-df-card-merchant-wrapper" style="position: absolute; bottom: 0; right: 0; background: transparent; padding: 8px 12px; z-index: 10;">                     \x3Cspan class="tg-df-card-merchant-pill" style="text-align: right; margin-bottom: 0;" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cdiv class="tg-df-card-desc-container" style="margin-bottom: 12px; position: relative;">                    \x3Cp class="tg-df-card-desc-content" style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 0; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;">${this.escapeHTML(deal.description)}${_p}                    \x3Cbutton type="button" class="tg-df-card-desc-btn" style="display: none; appearance: none; border: none; color: #000000; font-size: 11px; font-weight: 700; text-transform: uppercase; cursor: pointer; font-family: inherit; position: absolute; bottom: 2px; right: 0; background: linear-gradient(to right, transparent, #fff 20%, #fff); padding: 0 0 0 16px;" onclick="                      var c = this.parentNode;                      if (this.dataset.expanded === 'true') {                        var pd = (c.tagName === 'P') ? c : this.previousElementSibling;                        if (c.tagName === 'P') { c.parentNode.appendChild(this); pd = c; }                        pd.style.display = '-webkit-box';                        pd.style.webkitLineClamp = '3';                        this.textContent = 'READ MORE';                        this.style.position = 'absolute';                        this.style.background = 'linear-gradient(to right, transparent, #fff 20%, #fff)';                        this.style.paddingLeft = '16px';                        this.dataset.expanded = 'false';                      } else {                        var pd = this.previousElementSibling;                        pd.style.display = 'inline';                        pd.style.webkitLineClamp = 'unset';                        this.textContent = 'READ LESS';                        this.style.position = 'static';                        this.style.background = 'transparent';                        this.style.paddingLeft = '4px';                        this.dataset.expanded = 'true';                        pd.appendChild(this);                      }                    ">READ MORE${_button}                  \x3C/div>` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${deal.authorName ? `                    \x3Cdiv class="tg-df-author-line-desktop" style="padding: 0 0 ${isSavingsSquadMode ? 0 : 12}px 0;">                       \x3Cdiv style="display: flex; align-items: center; gap: 10px;">                          ${deal.authorImage ? `\x3Cimg src="${this.escapeHTML(deal.authorImage)}" alt="${this.escapeHTML(deal.authorName)}" class="tg-df-author-img" width="36" height="36" style="border-radius: 50%; object-fit: cover; flex-shrink: 0; aspect-ratio: 1 / 1; min-width: 36px; min-height: 36px;">` : ''}                          \x3Cdiv style="display: flex; flex-direction: column;">                             \x3Cdiv style="font-size: 10px; color: var(--tg-df-text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px; font-weight: 600;">Recommended by:${_div}                             \x3Cdiv style="font-size: 11px; color: var(--tg-df-text); line-height: 1.2;">                                \x3Cstrong>\x3Ca href="https://www.tomsguide.com/${this.escapeHTML(deal.documentUrl || '').replace(/^\/+/, '')}" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit; border-bottom: 1px dotted var(--tg-df-text-muted);">${this.escapeHTML(deal.authorName)}${_a}${_strong}                                ${deal.authorRole && !['null', 'nul', 'undefined'].includes(String(deal.authorRole).toLowerCase()) ? ` • ${this.escapeHTML(deal.authorRole)}` : ''}                                ${deal.modifiedDate ? `\x3Cspan style="color: var(--tg-df-text-muted);"> • ${getTimeAgo(deal.modifiedDate)}${_span}` : ''}                             ${_div}                          ${_div}                       ${_div}                    ${_div}                    ` : ''}                    ${priceGroupHtml}                  ${_div}                ${_div}                \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta" style="text-decoration: none; border-radius: 0;">${ctaText}${_a}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit) {            dealsHtml += `              \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer;">Load More${_button}              ${_div}            `;          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }          setTimeout(() => {            const contents = this.root.querySelectorAll('.tg-df-card-desc-content');            contents.forEach(p => {              if (p.scrollHeight > p.clientHeight || p.scrollHeight > 60) {                if (p.nextElementSibling) {                  p.nextElementSibling.style.display = 'block';                }              }            });                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');            document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));          }, 50);          const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              this.displayLimit += this.getRowsLimit();              this.render();            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/i-was-spending-usd7-a-day-at-coffee-shops-now-i-spend-usd1-thanks-to-my-nespresso-machine-thats-41-percent-off-at-amazon</link>
                                                                            <description>
                            <![CDATA[ Is your morning coffee routine getting too expensive? I recommend grabbing this Nespresso machine that's nearly $100 off on Amazon. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">Z6K9XtrM3uy5LTtMeDFvhR</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/vwmLMwL4yTWWgj23T9q46f-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 31 Jul 2026 16:22:20 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                                    <dc:creator><![CDATA[ Olivia Halevy ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/YAg7Fmh9dGKbg3hULBMWiU.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Olivia is a Deals Writer at Tom’s Guide. She loves all things lifestyle, shopping and sourcing the best deals from some of the top brands, retailers and websites around. From fashion and beauty to smart home and tech, she has an eye for finding practical, stylish, and reasonably-priced items that help make life simpler (and a little more aesthetically-pleasing).&amp;nbsp;Olivia has over five years of experience writing lifestyle, commerce, and branded content for &lt;a href=&quot;http://patch.com/&quot;&gt;Patch.com&lt;/a&gt;. She&#039;s covered major shopping holidays like Amazon Prime Day, Black Friday and Cyber Monday. In her free time, she enjoys trying out new exercise classes, binge-watching the latest viral TV shows, cooking, spending time with family and taking long walks with her dog, Gus.&amp;nbsp;&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/vwmLMwL4yTWWgj23T9q46f-1280-80.jpg">
                                                            <media:credit><![CDATA[Future / Olivia Halevy]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Nespresso]]></media:description>                                                            <media:text><![CDATA[Nespresso]]></media:text>
                                <media:title type="plain"><![CDATA[Nespresso]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/vwmLMwL4yTWWgj23T9q46f-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I love going to coffee shops, whether I'm just quickly grabbing my favorite vanilla oat milk latte, meeting up with a friend for coffee or getting some work done. However, every time I step foot inside a cafe, I'm spending at least $7 — and that's just for a coffee. </p><p>After calculating how much I was spending weekly and monthly, I was astonished. That's when I decided I needed to cut back a bit — and <a href="https://www.amazon.com/Nespresso-DeLonghi-ENV120GYAE-Espresso-Aeroccino/dp/B084GY5FHN/" target="_blank" rel="nofollow">my Nespresso Vertuo Next coffee and espresso maker</a> made that possible. Even better? It's currently 41% off on Amazon. </p><div class="product"><a data-dimension112="85336e50-8cea-11f1-9071-8d73434d97ba" data-action="Deal Block" data-label="Vertuo Next w Aeroccino Milk Frother" data-dimension48="Vertuo Next w Aeroccino Milk Frother" data-dimension25="$134" href="https://www.amazon.com/Nespresso-DeLonghi-ENV120GYAE-Espresso-Aeroccino/dp/B084GY5FHN/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:84.93%;"><img id="cqkR72BCgMgQiYg32kT4jW" name="Nespresso Vertuo" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/cqkR72BCgMgQiYg32kT4jW.jpg" mos="" align="middle" fullscreen="" width="1500" height="1274" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Nespresso-DeLonghi-ENV120GYAE-Espresso-Aeroccino/dp/B084GY5FHN/" target="_blank" rel="nofollow" data-dimension112="85336e50-8cea-11f1-9071-8d73434d97ba" data-action="Deal Block" data-label="Vertuo Next w Aeroccino Milk Frother" data-dimension48="Vertuo Next w Aeroccino Milk Frother" data-dimension25="$134">Vertuo Next w Aeroccino Milk Frother: was $229 now $134</a></strong><br>This Nespresso Vertuo Next machine can turn your home into a full on coffee shop. It supports six cup sizes including 5, 8, 12, and 18-oz., as well as single and double espressos brewed hot or over ice. The included Aeroccino frother provides hot or cold milk for your cappuccinos and lattes.<a class="view-deal button" href="https://www.amazon.com/Nespresso-DeLonghi-ENV120GYAE-Espresso-Aeroccino/dp/B084GY5FHN/" target="_blank" rel="nofollow" data-dimension112="85336e50-8cea-11f1-9071-8d73434d97ba" data-action="Deal Block" data-label="Vertuo Next w Aeroccino Milk Frother" data-dimension48="Vertuo Next w Aeroccino Milk Frother" data-dimension25="$134">View Deal</a></p></div><div class="product"><a data-dimension112="85336ef0-8cea-11f1-81ca-d9115b0a4f10" data-action="Deal Block" data-label="Capsules Vertuo" data-dimension48="Capsules Vertuo" data-dimension25="$42" href="https://www.amazon.com/Nespresso-Vertuoline-Seller-Assortment-Count/dp/B01N05APQY/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="Yvm4K4QWtX9JaZ3avkGG6L" name="Nespresso capsules" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Yvm4K4QWtX9JaZ3avkGG6L.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Nespresso-Vertuoline-Seller-Assortment-Count/dp/B01N05APQY/" target="_blank" rel="nofollow" data-dimension112="85336ef0-8cea-11f1-81ca-d9115b0a4f10" data-action="Deal Block" data-label="Capsules Vertuo" data-dimension48="Capsules Vertuo" data-dimension25="$42">Capsules Vertuo: $42</a></strong><br>These are the Nespresso capsules I buy on a monthly basis. The variety pack includes three different medium and dark roast coffee pods with rich flavors you'll love. They'll provide you with a 7.8 oz serving of coffee. <a class="view-deal button" href="https://www.amazon.com/Nespresso-Vertuoline-Seller-Assortment-Count/dp/B01N05APQY/" target="_blank" rel="nofollow" data-dimension112="85336ef0-8cea-11f1-81ca-d9115b0a4f10" data-action="Deal Block" data-label="Capsules Vertuo" data-dimension48="Capsules Vertuo" data-dimension25="$42">View Deal</a></p></div><p>At nearly $100 off, the Nespresso Vertuo Next is practically a steal — and it can quickly pay for itself compared to buying coffee from a café every day. I typically buy Nespresso Vertuo capsules, which cost $42 for a pack of 30. That works out to about $1.40 per cup, making it a much more affordable daily coffee habit than my typical coffee shop run.</p><p>The best part is, my Nespresso machine actually makes delicious coffees, lattes and cappuccinos, so I don't feel disappointed making coffee at home rather than buying one. The included Aeroccino Milk Frother is really the icing on the cake. I add my favorite oat milk to the frother and in seconds, I have creamy, frothy milk to add to my coffee or latte. </p><p>The machine is super easy to use — all you have to do is add your capsule, make sure the water tank is full and press the brewing button. You'll have a delicious, barista-style coffee in just seconds. You can also choose from six cup sizes including 5, 8, 12, and 18-oz., as well as single and double espressos brewed hot or over ice. The machine truly does it all! </p><p>Of course, I'll never fully stop indulging in my local coffee shops — but at least I can save a little bit of money by making my coffee at home a few days a week. If you're hoping to do the same, <a href="https://www.amazon.com/Nespresso-DeLonghi-ENV120GYAE-Espresso-Aeroccino/dp/B084GY5FHN/" target="_blank" rel="nofollow">I recommend grabbing the Nespresso Vertuo Next for just $134 on Amazon</a>. </p><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=coffee&view_mode=savings_squad&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function getTimeAgo(dateString) {        if (!dateString) return '';        const date = new Date(dateString);        const now = new Date();        const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);        if (diffInSeconds < 60) return 'Just now';        const diffInMinutes = Math.floor(diffInSeconds / 60);        if (diffInMinutes < 60) return `${diffInMinutes} min${diffInMinutes > 1 ? 's' : ''} ago`;        const diffInHours = Math.floor(diffInMinutes / 60);        if (diffInHours < 24) return `${diffInHours} hr${diffInHours > 1 ? 's' : ''} ago`;        const diffInDays = Math.floor(diffInHours / 24);        if (diffInDays < 30) return `${diffInDays} day${diffInDays > 1 ? 's' : ''} ago`;        const diffInMonths = Math.floor(diffInDays / 30);        if (diffInMonths < 12) return `${diffInMonths} mo${diffInMonths > 1 ? 's' : ''} ago`;        const diffInYears = Math.floor(diffInDays / 365);        return `${diffInYears} yr${diffInYears > 1 ? 's' : ''} ago`;      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;      position: relative;      z-index: 20;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      margin: 0 auto;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters-container {      position: relative;      width: 100%;      max-width: 800px;    }    .tg-df-scroll-btn {      display: none;      position: absolute;      top: 50%;      transform: translateY(-50%);      width: 32px;      height: 32px;      background: white;      border: 1px solid var(--tg-df-border);      border-radius: 50%;      align-items: center;      justify-content: center;      cursor: pointer;      z-index: 10;      box-shadow: 0 2px 8px rgba(0,0,0,0.1);      color: var(--tg-df-text-primary);      padding: 0;    }    .tg-df-scroll-btn svg {      width: 16px;      height: 16px;    }    .tg-df-scroll-btn:hover {      background: #f4f4f4;    }    .tg-df-scroll-btn.left {      left: 0px;    }    .tg-df-scroll-btn.right {      right: 0px;    }    @container tg-df (max-width: 768px) {      .tg-df-scroll-btn {        display: flex;        top: 22px; /* vertically center within the 44px high filter buttons */      }    }    .tg-df-filters {      display: grid;      width: 100%;      grid-template-columns: repeat(4, 1fr);      gap: 12px;      margin: 0 auto;      max-width: 800px;    }                 .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;      width: 100%;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      width: 100%;      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }    @container tg-df (max-width: 768px) {      .tg-df-filters-container {      position: relative;      width: 100%;      max-width: 800px;    }    .tg-df-scroll-btn {      display: none;      position: absolute;      top: 50%;      transform: translateY(-50%);      width: 32px;      height: 32px;      background: white;      border: 1px solid var(--tg-df-border);      border-radius: 50%;      align-items: center;      justify-content: center;      cursor: pointer;      z-index: 10;      box-shadow: 0 2px 8px rgba(0,0,0,0.1);      color: var(--tg-df-text-primary);      padding: 0;    }    .tg-df-scroll-btn svg {      width: 16px;      height: 16px;    }    .tg-df-scroll-btn:hover {      background: #f4f4f4;    }    .tg-df-scroll-btn.left {      left: 0px;    }    .tg-df-scroll-btn.right {      right: 0px;    }    @container tg-df (max-width: 768px) {      .tg-df-scroll-btn {        display: flex;        top: 22px; /* vertically center within the 44px high filter buttons */      }    }    .tg-df-filters {        width: 100%;        margin: 0;        margin-bottom: -320px;        padding: 0 16px 320px 16px;        display: flex;        flex-wrap: nowrap;        gap: 8px;        overflow-x: auto;        overflow-y: hidden;        pointer-events: none;        scrollbar-width: none;        -webkit-overflow-scrolling: touch;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }      .tg-df-sort-wrapper {        pointer-events: auto;        flex: 0 0 auto;        width: 175px;        min-width: 175px;      }    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 120px;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 16px 16px 8px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }                  .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;        width: auto;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;      width: 100vw;      max-width: 1200px;      position: relative;      left: 50%;      transform: translateX(-50%);    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 14px;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    .tg-df-grid-wrapper { position: relative; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn {      display: inline-flex;      align-items: center;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.40);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #1f69ff; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #0056e0; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }    .tg-df-savings-squad-header { margin-bottom: 24px; text-align: center; display: none; }    .tg-df-banner-img-desktop { display: block; width: 100%; height: auto; margin-bottom: 32px; }    .tg-df-banner-img-mobile { display: none; width: 100%; height: auto; margin-bottom: 32px; }    @container tg-df (max-width: 600px) {      .tg-df-banner-img-desktop { display: none; }      .tg-df-banner-img-mobile { display: block; }    }    .tg-df-header-title { font-size: 28px; font-weight: 700; color: var(--tg-df-text); margin: 32px 0 12px 0; line-height: 1.3; }    .tg-df-header-subtitle { font-size: 16px; color: var(--tg-df-text-muted); margin: 0 0 32px 0; line-height: 1.5; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3Cdiv class="tg-df-savings-squad-header" id="tg-df-savings-squad-header">      \x3Cpicture>        \x3Cimg src="https://cdn.mos.cms.futurecdn.net/flexiimages/xkh2og7m3d1778189998.png" alt="Deals Banner" class="tg-df-banner-img-desktop" />        \x3Cimg src="https://cdn.mos.cms.futurecdn.net/flexiimages/gmak6rtdf41778245089.png" alt="Deals Banner Mobile" class="tg-df-banner-img-mobile" />      \x3C/picture>      \x3Cdiv class="tg-df-header-text">        \x3Ch2 class="tg-df-header-title" id="tg-df-header-title">Editor's Choice Deals\x3C/h2>        \x3Cp class="tg-df-header-subtitle" id="tg-df-header-subtitle">Discover the best discounts currently available, curated daily by the Tom's Guide Savings Squad.\x3C/p>      \x3C/div>    \x3C/div>    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Televisions" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/wcMxTsHgqu3roMbAx7RLnT-132-100.png" alt="TVs" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">TVs\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Phones" data-pr="over50">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/G3KGaRGzj24F6PUsw4bWpT-132-100.png" alt="Phones" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Phones\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Computing" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/znNvsLzx8NEgNkD9HSFSnT-132-100.png" alt="Computing" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Computing\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Gaming" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/Pgew8yaRQeZFHqHjTzvBnT-132-100.png" alt="Gaming" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Gaming\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Mattresses" data-pr="over500">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/cW7xsaLyesxkHFVSiC4kmT-132-100.png" alt="Mattresses" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Mattresses\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Audio" data-pr="over30">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/pCvBVHuhaQVjKt3VgCjbqT-132-100.png" alt="Audio" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Audio\x3C/span>            \x3C/div>                  \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E      \x3Cdiv class="tg-df-top-bar" id="tg-df-top-bar" style="position: relative; z-index: 100; margin: 0 auto 20px;">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>      \x3C/div>    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">              \x3Cdiv class="tg-df-filters-container" style="position: relative; width: 100%; max-width: 800px; margin: 0 auto;">          \x3Cbutton class="tg-df-scroll-btn left" style="display: none;" onclick="this.parentElement.querySelector('.tg-df-filters').scrollBy({left: -200, behavior: 'smooth'})">            \x3Csvg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\x3Cpath d="M15 18l-6-6 6-6"/>\x3C/svg>          \x3C/button>          \x3Cbutton class="tg-df-scroll-btn right" style="display: none;" onclick="this.parentElement.querySelector('.tg-df-filters').scrollBy({left: 200, behavior: 'smooth'})">            \x3Csvg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\x3Cpath d="M9 18l6-6-6-6"/>\x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-filters">          \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>          \x3C/div>        \x3C/div>        \x3C/div>      \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();        }        getViewMode() {          console.log("DEBUG getViewMode -> override:", this.viewModeOverride, "editorMode:", this.editorMode);          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          console.log("[DEBUG] applyLayoutMode CALLED! mode=", mode);          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          const showHeaderDetails = params.get('show_header_details') !== 'false';          const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          const topBarDiv = this.root.querySelector('#tg-df-top-bar');          const headerElement = this.root.querySelector('#tg-df-savings-squad-header');          if (headerElement) {             headerElement.style.display = (mode === 'savings_squad' && showHeaderDetails) ? 'block' : 'none';          }          if (mode === 'carousel') {             this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (topBarDiv) topBarDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.add('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (topBarDiv) topBarDiv.style.display = 'block';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!this.viewModeOverride && initialViewMode) {            this.viewModeOverride = initialViewMode;          }          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Gaming laptops';          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (this.viewModeSelect && params.has('view_mode')) {            this.viewModeSelect.value = params.get('view_mode');          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'date_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }          const headerTitleEl = this.root.querySelector('#tg-df-header-title');          const headerSubtitleEl = this.root.querySelector('#tg-df-header-subtitle');          if (params.has('widget_title') && headerTitleEl) {             headerTitleEl.textContent = params.get('widget_title');          }          if (params.has('widget_subtitle') && headerSubtitleEl) {             headerSubtitleEl.textContent = params.get('widget_subtitle');          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            if (this.getViewMode() === 'savings_squad') {              this.searchInput.value = '';            } else {              this.searchInput.value = this.currentQuery;            }          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'date_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const handleFiltersScroll = () => {            const filters = this.root.querySelector('.tg-df-filters');            const leftBtn = this.root.querySelector('.tg-df-scroll-btn.left');            const rightBtn = this.root.querySelector('.tg-df-scroll-btn.right');            if (filters && leftBtn && rightBtn) {              const { scrollLeft, scrollWidth, clientWidth } = filters;              leftBtn.style.display = scrollLeft > 0 ? 'flex' : 'none';              rightBtn.style.display = Math.ceil(scrollLeft + clientWidth) < scrollWidth - 5 ? 'flex' : 'none';            }          };          const filters = this.root.querySelector('.tg-df-filters');          if (filters) {            filters.addEventListener('scroll', handleFiltersScroll);            window.addEventListener('resize', handleFiltersScroll);            setTimeout(handleFiltersScroll, 100);                        // Also call after rendering dropdowns            const origRenderCategories = this.renderCategories;            if (origRenderCategories) {               this.renderCategories = (...args) => {                 origRenderCategories.apply(this, args);                 setTimeout(handleFiltersScroll, 50);               };            }          }                const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          const showAutocomplete = () => {             if (this.getViewMode() !== 'savings_squad' || !this.autocompleteDropdown || !this.airedaleTags) return;                          let terms = this.airedaleTags;             if (this.airedaleBrands) {                terms = terms.concat(this.airedaleBrands.map(b => b.formatted_value));             }             terms = [...new Set(terms)];                          const query = this.searchInput.value.trim();             let matches = [];             if (query.length > 0) {                 matches = terms.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase());             } else {                 matches = terms;             }                          if (matches.length > 0) {                 this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                 this.autocompleteDropdown.classList.add('active');             } else {                 this.autocompleteDropdown.classList.remove('active');             }          };          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('focus', showAutocomplete);            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              showAutocomplete();              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  if (this.getViewMode() === 'savings_squad') {                    this.activeDealTag = null;                    this.currentQuery = '';                    if (this.categoryFilter) this.categoryFilter.value = 'all';                    this.fetchDeals('');                  } else {                    this.deals = [];                    this.render();                  }                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                                let isTag = false;                if (this.airedaleTags && this.airedaleTags.includes(query)) isTag = true;                if (this.airedaleBrands && this.airedaleBrands.some(b => b.formatted_value === query)) isTag = true;                this.activeDealTag = isTag ? query : null;                                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   if (query.length === 0 && this.getViewMode() === 'savings_squad') {                       if (this.categoryFilter) this.categoryFilter.value = 'all';                   }                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   if (this.categoryFilter && this.airedaleTags.includes(tag)) {                       this.categoryFilter.value = tag;                   }                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                            let isTag = false;              if (this.airedaleTags && this.airedaleTags.includes(query)) isTag = true;              if (this.airedaleBrands && this.airedaleBrands.some(b => b.formatted_value === query)) isTag = true;              this.activeDealTag = isTag ? query : null;                            this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 if (query.length === 0 && this.getViewMode() === 'savings_squad') {                     if (this.categoryFilter) this.categoryFilter.value = 'all';                 }                 this.fetchDeals(query);              }            });          }          if(this.sortSelect && this.sortSelect.querySelector('option[value="date_desc"]') === null) {              const option = document.createElement('option');              option.value = "date_desc";              option.text = "Newest First";              this.sortSelect.insertBefore(option, this.sortSelect.firstChild);          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               if (val) {                 this.currentQuery = val;               } else {                 if (this.searchInput && this.currentQuery === document.querySelector('#tg-df-brand-trigger')?.getAttribute('data-active-brand')) {                     // don't clear current query if a brand is selected                 } else if (this.searchInput) {                     this.currentQuery = '';                     this.searchInput.value = '';                 }               }               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = 'date_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }        async fetchDeals(query) {          this.showLoading();          this.deals = [];          this.displayLimit = this.getRowsLimit();                    try {            console.log("getViewMode returns:", this.getViewMode());            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad();            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query);               } else {                 await this.fetchHawkDeals(query);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const siteParam = this.website || 'tomsguide';            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1781000519267.json?site=${siteParam}&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=${siteParam}&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (!this.activeDealTag && this.currentQuery) {             const tagMatch = this.airedaleTags.find(t => t.toLowerCase() === this.currentQuery.toLowerCase());             if (tagMatch) {                this.activeDealTag = tagMatch;             }          }          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const siteParam = this.website || 'tomsguide';             const url = `https://airedale.futurecdn.net/feeds/feed_1781000519267.json?site=${siteParam}&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let seenUrls = new Set();                    let overallBrandsCounts = {};                    // First pass: extract ALL brands from topArticles so the dropdown has all options          topArticles.forEach((article) => {             if (!article.articlepage) return;             let pageData = [];             try { pageData = JSON.parse(article.articlepage[0]); } catch(e){ console.warn(e); }             const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');             savingsSquad.forEach((block) => {                const data = block.data || {};                if (data.brand) {                   const cleanBrand = data.brand.replace(/^\d+\.\s*/, '').trim();                   overallBrandsCounts[cleanBrand] = (overallBrandsCounts[cleanBrand] || 0) + 1;                }             });          });          targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                if (externalUrl) {                   if (seenUrls.has(externalUrl)) return;                  seenUrls.add(externalUrl);                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || '',                   authorName: article.articleauthortext ? article.articleauthortext[0] : (article.articleauthor ? article.articleauthor[0] : ''),                   authorRole: article.articleauthorrole ? article.articleauthorrole[0] : '',                   authorImage: article.articleauthormedia ? article.articleauthormedia[0] : '',                   documentUrl: article.documenturl ? article.documenturl[0] : '',                   modifiedDate: article.contentmodifieddate || article.modifieddate || ''                });             });          });                    const airedaleBrandsList = Object.keys(overallBrandsCounts).map(b => ({              formatted_value: b,              count: overallBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : 'date_desc';          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                ${_div}              ${_div}              \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin:0; border-radius:0;">${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)}${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = 'View Deal';              if (isSavingsSquadMode) {                priceGroupHtml = ``;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = isSavingsSquadMode ? `` : `                \x3Cdiv class="tg-df-card-price-group">                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description tg-df-card-desc-container" style="margin-bottom: 12px; position: relative;">                          \x3Cp class="tg-df-card-desc-content" style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 0; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;">${this.escapeHTML(deal.description)}${_p}                          \x3Cbutton type="button" class="tg-df-card-desc-btn" style="display: none; appearance: none; border: none; color: #000000; font-size: 11px; font-weight: 700; text-transform: uppercase; cursor: pointer; font-family: inherit; position: absolute; bottom: 2px; right: 0; background: linear-gradient(to right, transparent, #fff 20%, #fff); padding: 0 0 0 16px;" onclick="                            var c = this.parentNode;                            if (this.dataset.expanded === 'true') {                              var pd = (c.tagName === 'P') ? c : this.previousElementSibling;                              if (c.tagName === 'P') { c.parentNode.appendChild(this); pd = c; }                              pd.style.display = '-webkit-box';                              pd.style.webkitLineClamp = '3';                              this.textContent = 'READ MORE';                              this.style.position = 'absolute';                              this.style.background = 'linear-gradient(to right, transparent, #fff 20%, #fff)';                              this.style.paddingLeft = '16px';                              this.dataset.expanded = 'false';                            } else {                              var pd = this.previousElementSibling;                              pd.style.display = 'inline';                              pd.style.webkitLineClamp = 'unset';                              this.textContent = 'READ LESS';                              this.style.position = 'static';                              this.style.background = 'transparent';                              this.style.paddingLeft = '4px';                              this.dataset.expanded = 'true';                              pd.appendChild(this);                            }                          ">READ MORE${_button}                        \x3C/div>` : ''}                      ${_div}                    ${_div}                    ${deal.authorName ? `                      \x3Cdiv class="tg-df-author-line-mobile" style="padding: 0 0 12px 0; background: transparent;">                         \x3Cdiv style="display: flex; align-items: center; gap: 12px;">                            ${deal.authorImage ? `\x3Cimg src="${this.escapeHTML(deal.authorImage)}" alt="${this.escapeHTML(deal.authorName)}" class="tg-df-author-img" width="40" height="40" style="border-radius: 50%; object-fit: cover; flex-shrink: 0; aspect-ratio: 1 / 1; min-width: 40px; min-height: 40px;">` : ''}                            \x3Cdiv style="display: flex; flex-direction: column;">                               \x3Cdiv style="font-size: 10px; color: var(--tg-df-text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px; font-weight: 600;">\x3Cspan style="color: #FF6600;">${this.escapeHTML(deal.merchant)}${_span} deal recommended by:${_div}                               \x3Cdiv style="font-size: 11px; color: var(--tg-df-text); line-height: 1.3;">                                  \x3Cstrong>\x3Ca href="https://www.tomsguide.com/${this.escapeHTML(deal.documentUrl || '').replace(/^\/+/, '')}" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit; border-bottom: 1px dotted var(--tg-df-text-muted);">${this.escapeHTML(deal.authorName)}${_a}${_strong}                                  ${deal.authorRole && !['null', 'nul', 'undefined'].includes(String(deal.authorRole).toLowerCase()) ? ` • ${this.escapeHTML(deal.authorRole)}` : ''}                                  ${deal.modifiedDate ? `\x3Cdiv style="color: var(--tg-df-text-muted); margin-top: 2px;">${getTimeAgo(deal.modifiedDate)}${_div}` : ''}                               ${_div}                            ${_div}                         ${_div}                      ${_div}                    ` : ''}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>View Deal${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                  \x3Cdiv class="tg-df-card-merchant-wrapper" style="position: absolute; bottom: 0; right: 0; background: transparent; padding: 8px 12px; z-index: 10;">                     \x3Cspan class="tg-df-card-merchant-pill" style="text-align: right; margin-bottom: 0;" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cdiv class="tg-df-card-desc-container" style="margin-bottom: 12px; position: relative;">                    \x3Cp class="tg-df-card-desc-content" style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 0; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;">${this.escapeHTML(deal.description)}${_p}                    \x3Cbutton type="button" class="tg-df-card-desc-btn" style="display: none; appearance: none; border: none; color: #000000; font-size: 11px; font-weight: 700; text-transform: uppercase; cursor: pointer; font-family: inherit; position: absolute; bottom: 2px; right: 0; background: linear-gradient(to right, transparent, #fff 20%, #fff); padding: 0 0 0 16px;" onclick="                      var c = this.parentNode;                      if (this.dataset.expanded === 'true') {                        var pd = (c.tagName === 'P') ? c : this.previousElementSibling;                        if (c.tagName === 'P') { c.parentNode.appendChild(this); pd = c; }                        pd.style.display = '-webkit-box';                        pd.style.webkitLineClamp = '3';                        this.textContent = 'READ MORE';                        this.style.position = 'absolute';                        this.style.background = 'linear-gradient(to right, transparent, #fff 20%, #fff)';                        this.style.paddingLeft = '16px';                        this.dataset.expanded = 'false';                      } else {                        var pd = this.previousElementSibling;                        pd.style.display = 'inline';                        pd.style.webkitLineClamp = 'unset';                        this.textContent = 'READ LESS';                        this.style.position = 'static';                        this.style.background = 'transparent';                        this.style.paddingLeft = '4px';                        this.dataset.expanded = 'true';                        pd.appendChild(this);                      }                    ">READ MORE${_button}                  \x3C/div>` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${deal.authorName ? `                    \x3Cdiv class="tg-df-author-line-desktop" style="padding: 0 0 ${isSavingsSquadMode ? 0 : 12}px 0;">                       \x3Cdiv style="display: flex; align-items: center; gap: 10px;">                          ${deal.authorImage ? `\x3Cimg src="${this.escapeHTML(deal.authorImage)}" alt="${this.escapeHTML(deal.authorName)}" class="tg-df-author-img" width="36" height="36" style="border-radius: 50%; object-fit: cover; flex-shrink: 0; aspect-ratio: 1 / 1; min-width: 36px; min-height: 36px;">` : ''}                          \x3Cdiv style="display: flex; flex-direction: column;">                             \x3Cdiv style="font-size: 10px; color: var(--tg-df-text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px; font-weight: 600;">Recommended by:${_div}                             \x3Cdiv style="font-size: 11px; color: var(--tg-df-text); line-height: 1.2;">                                \x3Cstrong>\x3Ca href="https://www.tomsguide.com/${this.escapeHTML(deal.documentUrl || '').replace(/^\/+/, '')}" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit; border-bottom: 1px dotted var(--tg-df-text-muted);">${this.escapeHTML(deal.authorName)}${_a}${_strong}                                ${deal.authorRole && !['null', 'nul', 'undefined'].includes(String(deal.authorRole).toLowerCase()) ? ` • ${this.escapeHTML(deal.authorRole)}` : ''}                                ${deal.modifiedDate ? `\x3Cspan style="color: var(--tg-df-text-muted);"> • ${getTimeAgo(deal.modifiedDate)}${_span}` : ''}                             ${_div}                          ${_div}                       ${_div}                    ${_div}                    ` : ''}                    ${priceGroupHtml}                  ${_div}                ${_div}                \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta" style="text-decoration: none; border-radius: 0;">${ctaText}${_a}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit) {            dealsHtml += `              \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer;">Load More${_button}              ${_div}            `;          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }          setTimeout(() => {            const contents = this.root.querySelectorAll('.tg-df-card-desc-content');            contents.forEach(p => {              if (p.scrollHeight > p.clientHeight || p.scrollHeight > 60) {                if (p.nextElementSibling) {                  p.nextElementSibling.style.display = 'block';                }              }            });                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');            document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));          }, 50);          const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              this.displayLimit += this.getRowsLimit();              this.render();            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested De'Longhi's most expensive superautomatic espresso machine —and it's even got an app ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The De’Longhi PrimaDonna Aromatic is an ambitious superautomatic coffee machine. It can make 38 drinks and is even app-compatible, so you can control it from your phone (in theory). Is this enough to make it one of the <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">best espresso machines</a>?</p><p>Yes and no. The PrimaDonna Aromatic is extremely pricey, at a whopping $2,500, and I’m… not really sure why. You can get 80% of the same performance with the $1,500 De’Longhi Rivelia and — checks notes — 99.9% of the same performance with the $2,000 De’Longhi Eletta Explore. I’m not saying <em>don’t</em> get the PrimaDonna Aromatic — I’m saying you might be able to save your money without sacrificing quality here. </p><p>But, I digress. The PrimaDonna Aromatic is a fun, consistent, and functional coffee machine. Want to find out more? Keep reading this De’Longhi PrimaDonna Aromatic review. </p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-specs"><span>De'Longhi PrimaDonna Aromatic review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.delonghi.com/en-us/p/primadonna-aromatic-primadonna-aromatic-automatic-coffee-maker-ecam63075tm/ECAM63075TM.html" target="_blank" rel="nofollow">$2,499</a> / <a href="https://www.currys.co.uk/products/delonghi-prima-donna-aromatic-ecam630.75.tsm-smart-bean-to-cup-coffee-machine-silver-10283595.html" target="_blank" rel="nofollow">£1,549</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>27.7 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>18.2 x 10.3 x 16 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Grinder</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Heating system </strong></p></td><td  ><p>Not known</p></td></tr><tr><td class="firstcol " ><p><strong>Pressure </strong></p></td><td  ><p>19 bar</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank capacity</strong></p></td><td  ><p>60.9 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>Hot & cold milk frother, cleaning accessories, descaler, ice cube tray</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-price-availability"><span>De'Longhi PrimaDonna Aromatic review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9GWT4i7ZzeeHm5iacbMoXE" name="Delonghi_05.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/9GWT4i7ZzeeHm5iacbMoXE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The De’Longhi PrimaDonna Aromatic is objectively expensive. You’ll find it for <a href="https://www.delonghi.com/en-us/p/primadonna-aromatic-primadonna-aromatic-automatic-coffee-maker-ecam63075tm/ECAM63075TM.html" target="_blank" rel="nofollow">$2,499 from De’Longhi U.S.</a> and <a href="https://www.currys.co.uk/products/delonghi-prima-donna-aromatic-ecam630.75.tsm-smart-bean-to-cup-coffee-machine-silver-10283595.html" target="_blank" rel="nofollow">£1,549 from Currys in the U.K.</a> </p><p>If you’re wincing, you’re not alone. $2,499 makes the PrimaDonna Aromatic the most expensive fully automatic coffee machine made by De’Longhi. Yes, <em>the most expensive. </em>As you might already be able to guess, this is markedly more expensive than the <a href="https://www.tomsguide.com/home/home-appliances/delonghi-rivelia-review">De’Longhi Rivelia</a> ($1,499/£749), De’Longhi Magnifica Duo ($999). </p><p>However, it’s only (<em>only, </em>she types, while wincing) $499 more expensive than the <a href="https://www.tomsguide.com/home/home-appliances/kitchenaid-KF8-espresso-machine">KitchenAid KF8</a>, which I reviewed a couple of years ago and loved. </p><p>If you’re familiar with the world of superautomatic coffee machines, you might’ve come across Jura — its fully automatic models can go up to $5,499. I’ve tested the $2,699 <a href="https://www.tomsguide.com/home/coffee-makers/jura-e8-review">Jura E8</a>, and let me tell you — the PrimaDonna Aromatic is much better.</p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-design"><span>De'Longhi PrimaDonna Aromatic review: Design </span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="WaEdz7Fjciq34AMf5ruxXE" name="Delonghi_03.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/WaEdz7Fjciq34AMf5ruxXE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Like other automatic De’Longhi machines, the PrimaDonna Aromatic is a somewhat narrow but incredibly deep machine. It stretches a whopping 18 inches deep, so you’ll need to check your kitchen countertops can fit this behemoth before purchasing. </p><p>Even so, the 10-inch width is pretty good, even with the water tank on the side. You shouldn’t need to clear too much space if your counters are deep already. </p><p>And the PrimaDonna Aromatic also <em>looks</em> like other De’Longhi superautomatics. If I didn’t read the name, I could believe it was the Magnifica Duo or the Dinamica or the Eletta. All these machines are effectively big shiny squares. They’re not massively gorgeous, but they’re functional, unobtrusive, and get the job done. It obviously works, because De’Longhi machines are everywhere. </p><p>There’s also a bright LCD screen on the machine. Here, you can select your desired beverage — 38 recipes, some of which I’d never heard of — and customize your intensity and volume. Two buttons on either side of the machine configure hot and cold, so you can view only cold or only hot drinks. </p><h2 id="app">App</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kNnqn9LTFBaZfbYGTKSkWE" name="Delonghi_10.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/kNnqn9LTFBaZfbYGTKSkWE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I like apps when they’re actually useful. I like the Fellow app. I like the Acaia Brewmaster app. I like the apps for all the earbuds I test. But I do not like the De’Longhi My Coffee Lounge app. </p><p>I was prompted to complete a “coffee taste profile”, which told me I should get fruity coffee with notes of citrus, forest berry, and florals. I already knew this, though. It’s why I buy the same Ethiopian beans every time. </p><p>I suppose this is helpful for complete newbies — but I wouldn’t recommend complete newbies spend $2,500 on a coffee machine.</p><p>The app was also riddled with connectivity issues: There’s a Spotify-Wrapped-esque “Statistics” setting. This never loaded for me. You’re also supposed to be able to make your own recipes, but, again, these never loaded for me. The app just said “Couldn’t load any drink”. Hm, maybe because you won’t let me make any recipes? </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1600px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AYrE7GJfptpdNyQ5iSL8pT" name="delongghi primadonna aromatic" alt="three screenshots from the delonghi my coffee lounge app" src="https://cdn.mos.cms.futurecdn.net/AYrE7GJfptpdNyQ5iSL8pT.png" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: De'Longhi / Tom's Guide)</span></figcaption></figure><p>I like two parts of the My Coffee Lounge app: One, the “Health status” setting. Here you can see how many uses you have until you need to descale, how empty your water tank is, and how full your grounds drawer is. I love this. This is great for newbies. And “experts”. Everyone, actually.</p><p>And two: Suggested recipes. I got some super cool, super unique offerings here: Iced orange coffee, Iced raspberry tonic espresso, Iced espresso Mojito. Yes, please! I want more of this and fewer connectivity issues, thank you!</p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-espresso"><span>De'Longhi PrimaDonna Aromatic review: Espresso</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9gr6LjvVNo4yZcfiPHNqWE" name="Delonghi_04.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/9gr6LjvVNo4yZcfiPHNqWE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The De’Longhi PrimaDonna Aromatic espresso tastes like an automatic coffee machine’s espresso. That’s neither a good nor a bad thing depending on your preference. If you know you dislike automatic coffee, this wouldn’t be for you. If you know you like it, then great! </p><p>Here’s a photo of a shot of double espresso. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3681px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="uvPTHC9UVoNUAebxECBtk3" name="delonghi primadonna aromatic espresso" alt="coffee made on the de'longhi primadonna aromatic" src="https://cdn.mos.cms.futurecdn.net/uvPTHC9UVoNUAebxECBtk3.png" mos="" align="middle" fullscreen="" width="3681" height="2071" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the coffee has a lovely bright caramelly color, and there’s a layer of fluffy crema. Did it taste like the most nuanced, complex, scintillating coffee in the world? No. It was a little weak, but it had enough of a coffee flavor to be devoured by straightforward coffee fans. I would not recommend this machine for coffee snobs or espresso lovers, but if you’re a general coffee drinker who prioritizes ease-of-use over messing around with doses and flow rates, this would get the job done. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4382px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xkCivcjxwMjQuuZGm7qhr3" name="delonghi primadonna aromaticcold brew" alt="coffee made on the de'longhi primadonna aromatic" src="https://cdn.mos.cms.futurecdn.net/xkCivcjxwMjQuuZGm7qhr3.png" mos="" align="middle" fullscreen="" width="4382" height="2465" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>I also made a cold brew, which was, in all honesty, pretty nasty.  I wouldn’t recommend this for cold brew aficionados. The coffee was actually hot as it extracted, which means it’s also not “cold brew”. It’s more like an iced Americano. </p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-milk"><span>De'Longhi PrimaDonna Aromatic review: Milk</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5aPdZe973RRZAZkNWrtUXE" name="Delonghi_07.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/5aPdZe973RRZAZkNWrtUXE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Milk performance is what (mostly) separates the PrimaDonna Aromatic from other De’Longhi models. The PrimaDonna Aromatic comes with both a hot and cold milk frother, so you can have hot microfoam or cold foam. </p><p>The <a href="https://www.delonghi.com/en-us/p/eletta-explore-eletta-explore-espresso-machine/ECAM45086S+EX%3A4.html?pid=0132250196">De’Longhi Eletta Explore</a> ($1,999) also has this capability, so it might be worth checking out that model if you want to save some $$$. However, the Eletta Explore has a smaller water tank capacity. Again, not sure that’s worth an extra $500, but some food for thought. </p><p>Here’s a photo of a drink. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5117px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="JYsUS54wYBpHdSGecLjiu3" name="delonghi primadonna aromatic milk" alt="coffee made on the de'longhi primadonna aromatic" src="https://cdn.mos.cms.futurecdn.net/JYsUS54wYBpHdSGecLjiu3.png" mos="" align="middle" fullscreen="" width="5117" height="2879" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the milk is smooth and evenly textured. Hot foam tends to deflate quite rapidly, so I’d recommend not leaving your cappuccinos around before drinking. Get ‘em while they’re hot! </p><p>This milk texture is around the same quality as the De’Longhi Rivelia, but that model doesn’t come with cold and hot — you’d have to buy the <a href="https://www.delonghi.com/en-us/p/automatic-milk-jugs-dlsc032-lattecrema-cool-upgrade-set/DLSC032.html?pid=AS00006309">cool version separately</a> ($129).</p><p>I really like the milk function on the PrimaDonna Aromatic. It has great texture, it’s easy to clean, and it comes with all the necessary accessories (unlike Jura). </p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-storage-maintenance"><span>De'Longhi PrimaDonna Aromatic review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rsBEzxYgsJESzWfDtDJXXE" name="Delonghi_09.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/rsBEzxYgsJESzWfDtDJXXE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The PrimaDonna Aromatic is easy to clean. The app tells you exactly when to clean, and I would recommend cleaning out the drip tray every day at a minimum. </p><p>My only major qualm is that you have to empty the grounds drawer more often than you think. I emptied it after around 3-4 drinks each time, and the drawer was full of ground beans each time. I don’t think this machine is particularly economical in terms of coffee and water usage, but that’s true of almost all automatic machines. </p><p>In terms of longevity, you get a 2-year warranty as standard, which goes up to 3 years with registration. Comparatively, Breville offers a 2-year warranty, as does Ninja.</p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-verdict"><span>De'Longhi PrimaDonna Aromatic review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8TB4KShMnijxRDsgGe7rqD" name="Delonghi_08.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/8TB4KShMnijxRDsgGe7rqD.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The PrimaDonna Aromatic prioritizes simplicity and speed over coffee snobbery. It’s perfect for automatic machine users who want coffee and want it now. Would I buy it? Probably not. Would I recommend it for people who love Starbucks and Dunkin’ coffee and want to make drinks easily at home? 100%.</p><p>However, I’m still not totally sure what separates it from De’Longhi’s $1,999 Eletta Explore — it has Bean Adapt, De’Longhi’s My Coffee Lounge app connectivity, and 50 recipes compared to the PrimaDonna Aromatic’s 38. I think I’d recommend saving your $500 and getting the Eletta Explore — or save $1,000 and get the De’Longhi Rivelia. </p><p>But if you have $2,500 to spend, by all means, go for it. The PrimaDonna Aromatic is a great machine capable of making consistent espresso over and over. It’s just <em>very </em>expensive. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/delonghi-primadonna-aromatic-review</link>
                                                                            <description>
                            <![CDATA[ The new De'Longhi PrimaDonna Aromatic can make iced and hot drinks in seconds, has a range of recipes in the app, and looks super sleek. It's just... pricey. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">pCpVtkPjHVoWk8kHvFWBJG</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/PHMqX3gMv3WkGXzsqV6icD-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 31 Jul 2026 13:58:07 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/PHMqX3gMv3WkGXzsqV6icD-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the de&#039;longhi primadonna aromatic photographed against the blue tom&#039;s guide background]]></media:description>                                                            <media:text><![CDATA[the de&#039;longhi primadonna aromatic photographed against the blue tom&#039;s guide background]]></media:text>
                                <media:title type="plain"><![CDATA[the de&#039;longhi primadonna aromatic photographed against the blue tom&#039;s guide background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/PHMqX3gMv3WkGXzsqV6icD-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The De’Longhi PrimaDonna Aromatic is an ambitious superautomatic coffee machine. It can make 38 drinks and is even app-compatible, so you can control it from your phone (in theory). Is this enough to make it one of the <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">best espresso machines</a>?</p><p>Yes and no. The PrimaDonna Aromatic is extremely pricey, at a whopping $2,500, and I’m… not really sure why. You can get 80% of the same performance with the $1,500 De’Longhi Rivelia and — checks notes — 99.9% of the same performance with the $2,000 De’Longhi Eletta Explore. I’m not saying <em>don’t</em> get the PrimaDonna Aromatic — I’m saying you might be able to save your money without sacrificing quality here. </p><p>But, I digress. The PrimaDonna Aromatic is a fun, consistent, and functional coffee machine. Want to find out more? Keep reading this De’Longhi PrimaDonna Aromatic review. </p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-specs"><span>De'Longhi PrimaDonna Aromatic review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.delonghi.com/en-us/p/primadonna-aromatic-primadonna-aromatic-automatic-coffee-maker-ecam63075tm/ECAM63075TM.html" target="_blank" rel="nofollow">$2,499</a> / <a href="https://www.currys.co.uk/products/delonghi-prima-donna-aromatic-ecam630.75.tsm-smart-bean-to-cup-coffee-machine-silver-10283595.html" target="_blank" rel="nofollow">£1,549</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>27.7 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>18.2 x 10.3 x 16 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Grinder</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Heating system </strong></p></td><td  ><p>Not known</p></td></tr><tr><td class="firstcol " ><p><strong>Pressure </strong></p></td><td  ><p>19 bar</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank capacity</strong></p></td><td  ><p>60.9 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>Hot & cold milk frother, cleaning accessories, descaler, ice cube tray</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-price-availability"><span>De'Longhi PrimaDonna Aromatic review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9GWT4i7ZzeeHm5iacbMoXE" name="Delonghi_05.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/9GWT4i7ZzeeHm5iacbMoXE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The De’Longhi PrimaDonna Aromatic is objectively expensive. You’ll find it for <a href="https://www.delonghi.com/en-us/p/primadonna-aromatic-primadonna-aromatic-automatic-coffee-maker-ecam63075tm/ECAM63075TM.html" target="_blank" rel="nofollow">$2,499 from De’Longhi U.S.</a> and <a href="https://www.currys.co.uk/products/delonghi-prima-donna-aromatic-ecam630.75.tsm-smart-bean-to-cup-coffee-machine-silver-10283595.html" target="_blank" rel="nofollow">£1,549 from Currys in the U.K.</a> </p><p>If you’re wincing, you’re not alone. $2,499 makes the PrimaDonna Aromatic the most expensive fully automatic coffee machine made by De’Longhi. Yes, <em>the most expensive. </em>As you might already be able to guess, this is markedly more expensive than the <a href="https://www.tomsguide.com/home/home-appliances/delonghi-rivelia-review">De’Longhi Rivelia</a> ($1,499/£749), De’Longhi Magnifica Duo ($999). </p><p>However, it’s only (<em>only, </em>she types, while wincing) $499 more expensive than the <a href="https://www.tomsguide.com/home/home-appliances/kitchenaid-KF8-espresso-machine">KitchenAid KF8</a>, which I reviewed a couple of years ago and loved. </p><p>If you’re familiar with the world of superautomatic coffee machines, you might’ve come across Jura — its fully automatic models can go up to $5,499. I’ve tested the $2,699 <a href="https://www.tomsguide.com/home/coffee-makers/jura-e8-review">Jura E8</a>, and let me tell you — the PrimaDonna Aromatic is much better.</p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-design"><span>De'Longhi PrimaDonna Aromatic review: Design </span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="WaEdz7Fjciq34AMf5ruxXE" name="Delonghi_03.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/WaEdz7Fjciq34AMf5ruxXE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Like other automatic De’Longhi machines, the PrimaDonna Aromatic is a somewhat narrow but incredibly deep machine. It stretches a whopping 18 inches deep, so you’ll need to check your kitchen countertops can fit this behemoth before purchasing. </p><p>Even so, the 10-inch width is pretty good, even with the water tank on the side. You shouldn’t need to clear too much space if your counters are deep already. </p><p>And the PrimaDonna Aromatic also <em>looks</em> like other De’Longhi superautomatics. If I didn’t read the name, I could believe it was the Magnifica Duo or the Dinamica or the Eletta. All these machines are effectively big shiny squares. They’re not massively gorgeous, but they’re functional, unobtrusive, and get the job done. It obviously works, because De’Longhi machines are everywhere. </p><p>There’s also a bright LCD screen on the machine. Here, you can select your desired beverage — 38 recipes, some of which I’d never heard of — and customize your intensity and volume. Two buttons on either side of the machine configure hot and cold, so you can view only cold or only hot drinks. </p><h2 id="app">App</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kNnqn9LTFBaZfbYGTKSkWE" name="Delonghi_10.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/kNnqn9LTFBaZfbYGTKSkWE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I like apps when they’re actually useful. I like the Fellow app. I like the Acaia Brewmaster app. I like the apps for all the earbuds I test. But I do not like the De’Longhi My Coffee Lounge app. </p><p>I was prompted to complete a “coffee taste profile”, which told me I should get fruity coffee with notes of citrus, forest berry, and florals. I already knew this, though. It’s why I buy the same Ethiopian beans every time. </p><p>I suppose this is helpful for complete newbies — but I wouldn’t recommend complete newbies spend $2,500 on a coffee machine.</p><p>The app was also riddled with connectivity issues: There’s a Spotify-Wrapped-esque “Statistics” setting. This never loaded for me. You’re also supposed to be able to make your own recipes, but, again, these never loaded for me. The app just said “Couldn’t load any drink”. Hm, maybe because you won’t let me make any recipes? </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1600px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AYrE7GJfptpdNyQ5iSL8pT" name="delongghi primadonna aromatic" alt="three screenshots from the delonghi my coffee lounge app" src="https://cdn.mos.cms.futurecdn.net/AYrE7GJfptpdNyQ5iSL8pT.png" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: De'Longhi / Tom's Guide)</span></figcaption></figure><p>I like two parts of the My Coffee Lounge app: One, the “Health status” setting. Here you can see how many uses you have until you need to descale, how empty your water tank is, and how full your grounds drawer is. I love this. This is great for newbies. And “experts”. Everyone, actually.</p><p>And two: Suggested recipes. I got some super cool, super unique offerings here: Iced orange coffee, Iced raspberry tonic espresso, Iced espresso Mojito. Yes, please! I want more of this and fewer connectivity issues, thank you!</p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-espresso"><span>De'Longhi PrimaDonna Aromatic review: Espresso</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9gr6LjvVNo4yZcfiPHNqWE" name="Delonghi_04.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/9gr6LjvVNo4yZcfiPHNqWE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The De’Longhi PrimaDonna Aromatic espresso tastes like an automatic coffee machine’s espresso. That’s neither a good nor a bad thing depending on your preference. If you know you dislike automatic coffee, this wouldn’t be for you. If you know you like it, then great! </p><p>Here’s a photo of a shot of double espresso. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3681px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="uvPTHC9UVoNUAebxECBtk3" name="delonghi primadonna aromatic espresso" alt="coffee made on the de'longhi primadonna aromatic" src="https://cdn.mos.cms.futurecdn.net/uvPTHC9UVoNUAebxECBtk3.png" mos="" align="middle" fullscreen="" width="3681" height="2071" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the coffee has a lovely bright caramelly color, and there’s a layer of fluffy crema. Did it taste like the most nuanced, complex, scintillating coffee in the world? No. It was a little weak, but it had enough of a coffee flavor to be devoured by straightforward coffee fans. I would not recommend this machine for coffee snobs or espresso lovers, but if you’re a general coffee drinker who prioritizes ease-of-use over messing around with doses and flow rates, this would get the job done. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4382px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xkCivcjxwMjQuuZGm7qhr3" name="delonghi primadonna aromaticcold brew" alt="coffee made on the de'longhi primadonna aromatic" src="https://cdn.mos.cms.futurecdn.net/xkCivcjxwMjQuuZGm7qhr3.png" mos="" align="middle" fullscreen="" width="4382" height="2465" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>I also made a cold brew, which was, in all honesty, pretty nasty.  I wouldn’t recommend this for cold brew aficionados. The coffee was actually hot as it extracted, which means it’s also not “cold brew”. It’s more like an iced Americano. </p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-milk"><span>De'Longhi PrimaDonna Aromatic review: Milk</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5aPdZe973RRZAZkNWrtUXE" name="Delonghi_07.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/5aPdZe973RRZAZkNWrtUXE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Milk performance is what (mostly) separates the PrimaDonna Aromatic from other De’Longhi models. The PrimaDonna Aromatic comes with both a hot and cold milk frother, so you can have hot microfoam or cold foam. </p><p>The <a href="https://www.delonghi.com/en-us/p/eletta-explore-eletta-explore-espresso-machine/ECAM45086S+EX%3A4.html?pid=0132250196">De’Longhi Eletta Explore</a> ($1,999) also has this capability, so it might be worth checking out that model if you want to save some $$$. However, the Eletta Explore has a smaller water tank capacity. Again, not sure that’s worth an extra $500, but some food for thought. </p><p>Here’s a photo of a drink. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5117px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="JYsUS54wYBpHdSGecLjiu3" name="delonghi primadonna aromatic milk" alt="coffee made on the de'longhi primadonna aromatic" src="https://cdn.mos.cms.futurecdn.net/JYsUS54wYBpHdSGecLjiu3.png" mos="" align="middle" fullscreen="" width="5117" height="2879" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the milk is smooth and evenly textured. Hot foam tends to deflate quite rapidly, so I’d recommend not leaving your cappuccinos around before drinking. Get ‘em while they’re hot! </p><p>This milk texture is around the same quality as the De’Longhi Rivelia, but that model doesn’t come with cold and hot — you’d have to buy the <a href="https://www.delonghi.com/en-us/p/automatic-milk-jugs-dlsc032-lattecrema-cool-upgrade-set/DLSC032.html?pid=AS00006309">cool version separately</a> ($129).</p><p>I really like the milk function on the PrimaDonna Aromatic. It has great texture, it’s easy to clean, and it comes with all the necessary accessories (unlike Jura). </p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-storage-maintenance"><span>De'Longhi PrimaDonna Aromatic review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rsBEzxYgsJESzWfDtDJXXE" name="Delonghi_09.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/rsBEzxYgsJESzWfDtDJXXE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The PrimaDonna Aromatic is easy to clean. The app tells you exactly when to clean, and I would recommend cleaning out the drip tray every day at a minimum. </p><p>My only major qualm is that you have to empty the grounds drawer more often than you think. I emptied it after around 3-4 drinks each time, and the drawer was full of ground beans each time. I don’t think this machine is particularly economical in terms of coffee and water usage, but that’s true of almost all automatic machines. </p><p>In terms of longevity, you get a 2-year warranty as standard, which goes up to 3 years with registration. Comparatively, Breville offers a 2-year warranty, as does Ninja.</p><h2 class="article-body__section" id="section-de-longhi-primadonna-aromatic-review-verdict"><span>De'Longhi PrimaDonna Aromatic review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8TB4KShMnijxRDsgGe7rqD" name="Delonghi_08.JPG" alt="the de'longhi primadonna aromatic photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/8TB4KShMnijxRDsgGe7rqD.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The PrimaDonna Aromatic prioritizes simplicity and speed over coffee snobbery. It’s perfect for automatic machine users who want coffee and want it now. Would I buy it? Probably not. Would I recommend it for people who love Starbucks and Dunkin’ coffee and want to make drinks easily at home? 100%.</p><p>However, I’m still not totally sure what separates it from De’Longhi’s $1,999 Eletta Explore — it has Bean Adapt, De’Longhi’s My Coffee Lounge app connectivity, and 50 recipes compared to the PrimaDonna Aromatic’s 38. I think I’d recommend saving your $500 and getting the Eletta Explore — or save $1,000 and get the De’Longhi Rivelia. </p><p>But if you have $2,500 to spend, by all means, go for it. The PrimaDonna Aromatic is a great machine capable of making consistent espresso over and over. It’s just <em>very </em>expensive. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I put a battery-powered induction stove to the test — here’s what surprised me ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The oil sizzled as I placed the scallops in the pan. On the counter next to me sat a tray of just-baked chocolate chip cookies, their aroma wafting through the air. In the oven, a tray of chicken thighs was slowly baking atop a mixture of Chinese vegetables, their juices infusing with the cabbage and boy choy beneath.</p><p>I was cooking it all on the Copper Charlie, an innovative induction stove that doesn’t require any special installation, thanks to a massive battery. And while it's an early adopter in the world of early adopters, it could represent the future of home appliances. </p><h2 id="renovation-is-the-mother-of-necessity">Renovation is the mother of necessity</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5CZATxc8HsbQamB9K4HAUe" name="Copper Charlie cooktop" alt="Copper Charlie cooktop" src="https://cdn.mos.cms.futurecdn.net/5CZATxc8HsbQamB9K4HAUe.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>I tested out the range in the Brooklyn apartment of Ted Booth, who switched from a traditional gas oven to the Copper about a year ago. At the time, his building was facing a million-dollar renovation bill to upgrade the ancient gas pipes leading into, and threading through, the entire 33-unit building.</p><p>Rather than forcing the entire building to front the cost of fixing each unit’s gas line, the condo board gave each owner an option to switch to an induction stove. However, most induction stoves require a 220-volt electric line — which would have meant an equally expensive bill. </p><p>But there was an alternative: the Copper induction stove. This range has a large battery in the bottom, where the warming tray would sit. Not only does this provide a steady source of energy, even when the power goes out, but it also allows the stove to use a regular 110-volt outlet, so you can plug it in almost anywhere. </p><p>“To me, it was an opportunity to get off gas,” Booth said. “And, you can call me a bit of an early adopter, so it appealed to me that way.”</p><h3 class="article-body__section" id="section-modern-but-retro"><span>Modern but retro</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4NxvG2P32duYnS62Mc5dFJ" name="Copper Charlie knobs" alt="Copper Charlie knobs" src="https://cdn.mos.cms.futurecdn.net/4NxvG2P32duYnS62Mc5dFJ.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><div><blockquote><p>"It's the most analog experience I've had with a tech product"</p><p>— Ted Booth</p></blockquote></div><p>While the Charlie uses a very new type of technology, and has Wi-Fi and can be updated over the internet, it’s surprisingly low on connected features. </p><p>“I like how simple it is,” Booth said. “It's connected to the Wi-Fi, it has a battery, but it's the most analog experience I've had with a tech product. You have the little light indicator on the side, and the screen shows you what it's doing — it's 350 degrees. That's all you need to know. It's great.  And then the two little switches for the light and convection.”</p><h3 class="article-body__section" id="section-the-cost-of-upgrading"><span>The cost of upgrading</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4032px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YtFxSHTSXqXwf62mnYZtBP" name="Copper Charlie oven open" alt="Copper Charlie oven open" src="https://cdn.mos.cms.futurecdn.net/YtFxSHTSXqXwf62mnYZtBP.jpg" mos="" align="middle" fullscreen="" width="4032" height="2268" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Copper Charlie isn’t cheap. The <a href="https://copperhome.com/products/charlie?variant=42413374079069" target="_blank">base model, with black plastic knobs, costs $6,000</a>, while the model with black walnut knobs is $6,300. That’s more than twice as expensive as some popular models from <a href="https://www.bestbuy.com/product/lg-30-in-wide-6-3-cu-ft-slide-in-electric-induction-true-convect-range-with-5-elements-instaview-and-flex-control-knobs-stainless-steel/J7G5658LSF"><u>LG</u></a> and GE — though it should be noted the GE Cafe series can get up to $5,000. </p><p>The Charlie is not Energy Star-certified, so you can’t get any government rebates from that program, but there are several other local clean energy tax credits that can help ease the cost.</p><p>However, it did get the seal of approval from the New York City Housing Authority, which is installing <a href="https://www.nytimes.com/2026/01/16/nyregion/induction-stoves-housing-authority.html"><u>100 Copper Charlie stoves in apartments</u></a> throughout the city as a pilot program for lower-income residents, as all city-owned apartment buildings get converted from gas to electric.</p><p>“There are so many families that haven't had the ability to cook for years because they had their utility to get the gas shut off,” said Lisa Pickney, a professional chef who created the Official Dessert of the 1996 Atlanta Olympics, served on the Obama White House culinary team, and now works for Copper. </p><p>“And so they just gave them a hot plate and a rice cooker and a teapot. And it's just not right. They can't make a Thanksgiving turkey. So that kind of broke my heart. And now they'll be able to have Thanksgiving and do proper cooking now.” </p><p>The Copper Charlie isn’t the only induction stove of its kind; a rival company, the <a href="https://www.tomsguide.com/home/home-appliances/i-tried-out-an-induction-stove-that-works-even-when-the-power-goes-out"><u>Electra Induction Stove</u></a>, works in much the same way. Rather than the lithium ion batteries you might find in your phone or electric scooter, these stoves use a lithium phosphate battery, which is more stable, and more suited for how it’s used with the stoves. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OqREmX"></div>                            </div>                            <script src="https://kwizly.com/embed/OqREmX.js" async></script><h3 class="article-body__section" id="section-now-we-re-cooking"><span>Now we're cooking</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Cv4aYBK8AB99GeMZm3MFmU" name="Copper Charlie scallops" alt="searing scallops on the Copper Charlie" src="https://cdn.mos.cms.futurecdn.net/Cv4aYBK8AB99GeMZm3MFmU.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>As I (well, mostly Lisa) were cooking, I was impressed with how fast the oven heated up. While I didn’t time it, the Copper Charlie seemed far shorter than my oven at home. Similarly, its cooktop element warmed up the pan in no time, and put a sear on the scallops as good as I’ve ever done on any range, gas or electric.</p><p>Booth said that his cooking game has improved since getting the Charlie. “I like to make a Spanish potato omelet,” he said. “I like it because you can get a really nice, low temperature, so it doesn't brown too quickly. With the gas, I could get a low flame, but then the heat doesn't distribute quite as well, and so it's like cooking more in one spot and less on the edges.” </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:57.65%;"><img id="7in93fN4mpP8rnadB7BmuY" name="Copper Charlie chicken" alt="Chicken baked in the Copper Charlie" src="https://cdn.mos.cms.futurecdn.net/7in93fN4mpP8rnadB7BmuY.jpg" mos="" align="middle" fullscreen="" width="2000" height="1153" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Booth said he could no longer do the <a href="https://bittmanproject.com/recipe/seared-steak/" target="_blank">Mark Bittman trick of cooking a steak</a> by placing a pan on the very bottom of the oven, but the Charlie proved its worth when the outlet behind the stove stopped working. </p><p>“At first, I was like, what are we gonna do?” he said. “It's gonna take a while to get an electrician to come, and then I was like, oh, no, wait, it's fine. We have an outlet behind that cluster of little appliances in the corner is a four-prong outlet with a GFCI. So I just plugged it into that.”</p><p>“That to me really proved to me how much power this draws, because we've got an Instapot, we've got a kettle, we've got an espresso machine.  And we never had an issue, it never popped the GFCI.”</p><h3 class="article-body__section" id="section-how-it-s-working-out"><span>How it's working out</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="js4rQ6VnrMbiGjMoaYsBZ4" name="Copper Charlie induction range" alt="Copper Charlie induction range" src="https://cdn.mos.cms.futurecdn.net/js4rQ6VnrMbiGjMoaYsBZ4.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Ultimately, eleven owners in Booth’s building ended up getting a Copper Charlie. It proved to be more expensive than those who merely opted to replace their gas line, but Booth said it was worth the expense.</p><p>“We're gonna spend all this money for these pipes in the wall, and all I get is to get a blue flame?” he asks. ”I want something better than that.”</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/I-tried-an-induction-cooktop-3-things-i-learned">I used an induction cooktop for the first time — here are 3 things I learned</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/mistakes-i-made-when-i-switched-to-an-induction-cooktop-and-how-to-avoid-them">5 mistakes I made when I switched to an induction cooktop</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/we-tested-3-portable-induction-cooktops-at-wildly-different-prices-heres-our-verdict">We tested 3 portable induction cooktops from $50 to $1,500 </a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/i-put-a-battery-powered-induction-stove-to-the-test-heres-what-surprised-me</link>
                                                                            <description>
                            <![CDATA[ I tested the Copper Charlie, an induction range with a built-in battery, which makes it far easier to install — and even works when the power is out. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">DdYjBJKiUFNpyXNePiyb4A</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/g8TQNn5XqY4EknpbYVuy3n-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 31 Jul 2026 09:45:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ mike.prospero@futurenet.com (Mike Prospero) ]]></author>                    <dc:creator><![CDATA[ Mike Prospero ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xeJFBrJwXQzCqXLZXBFBKQ.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael A. Prospero is the U.S. Editor-in-Chief for Tom’s Guide.  In his spare time, he also tests out the latest drones, electric scooters, and smart home gadgets, such as video doorbells. Before his tenure at Tom&#039;s Guide, he was the Reviews Editor for Laptop Magazine, a reporter at Fast Company, the Times of Trenton, and, many eons back, an intern at George magazine. He received his undergraduate degree from Boston College, where he worked on the campus newspaper The Heights, and then attended the Columbia University school of Journalism. When he’s not testing out the latest running watch, electric scooter, or skiing or training for a marathon, he’s probably using the latest sous vide machine, smoker, or pizza oven, to the delight — or chagrin — of his family.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/g8TQNn5XqY4EknpbYVuy3n-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Copper Charlie induction range]]></media:description>                                                            <media:text><![CDATA[Copper Charlie induction range]]></media:text>
                                <media:title type="plain"><![CDATA[Copper Charlie induction range]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/g8TQNn5XqY4EknpbYVuy3n-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The oil sizzled as I placed the scallops in the pan. On the counter next to me sat a tray of just-baked chocolate chip cookies, their aroma wafting through the air. In the oven, a tray of chicken thighs was slowly baking atop a mixture of Chinese vegetables, their juices infusing with the cabbage and boy choy beneath.</p><p>I was cooking it all on the Copper Charlie, an innovative induction stove that doesn’t require any special installation, thanks to a massive battery. And while it's an early adopter in the world of early adopters, it could represent the future of home appliances. </p><h2 id="renovation-is-the-mother-of-necessity">Renovation is the mother of necessity</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5CZATxc8HsbQamB9K4HAUe" name="Copper Charlie cooktop" alt="Copper Charlie cooktop" src="https://cdn.mos.cms.futurecdn.net/5CZATxc8HsbQamB9K4HAUe.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>I tested out the range in the Brooklyn apartment of Ted Booth, who switched from a traditional gas oven to the Copper about a year ago. At the time, his building was facing a million-dollar renovation bill to upgrade the ancient gas pipes leading into, and threading through, the entire 33-unit building.</p><p>Rather than forcing the entire building to front the cost of fixing each unit’s gas line, the condo board gave each owner an option to switch to an induction stove. However, most induction stoves require a 220-volt electric line — which would have meant an equally expensive bill. </p><p>But there was an alternative: the Copper induction stove. This range has a large battery in the bottom, where the warming tray would sit. Not only does this provide a steady source of energy, even when the power goes out, but it also allows the stove to use a regular 110-volt outlet, so you can plug it in almost anywhere. </p><p>“To me, it was an opportunity to get off gas,” Booth said. “And, you can call me a bit of an early adopter, so it appealed to me that way.”</p><h3 class="article-body__section" id="section-modern-but-retro"><span>Modern but retro</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4NxvG2P32duYnS62Mc5dFJ" name="Copper Charlie knobs" alt="Copper Charlie knobs" src="https://cdn.mos.cms.futurecdn.net/4NxvG2P32duYnS62Mc5dFJ.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><div><blockquote><p>"It's the most analog experience I've had with a tech product"</p><p>— Ted Booth</p></blockquote></div><p>While the Charlie uses a very new type of technology, and has Wi-Fi and can be updated over the internet, it’s surprisingly low on connected features. </p><p>“I like how simple it is,” Booth said. “It's connected to the Wi-Fi, it has a battery, but it's the most analog experience I've had with a tech product. You have the little light indicator on the side, and the screen shows you what it's doing — it's 350 degrees. That's all you need to know. It's great.  And then the two little switches for the light and convection.”</p><h3 class="article-body__section" id="section-the-cost-of-upgrading"><span>The cost of upgrading</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4032px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YtFxSHTSXqXwf62mnYZtBP" name="Copper Charlie oven open" alt="Copper Charlie oven open" src="https://cdn.mos.cms.futurecdn.net/YtFxSHTSXqXwf62mnYZtBP.jpg" mos="" align="middle" fullscreen="" width="4032" height="2268" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Copper Charlie isn’t cheap. The <a href="https://copperhome.com/products/charlie?variant=42413374079069" target="_blank">base model, with black plastic knobs, costs $6,000</a>, while the model with black walnut knobs is $6,300. That’s more than twice as expensive as some popular models from <a href="https://www.bestbuy.com/product/lg-30-in-wide-6-3-cu-ft-slide-in-electric-induction-true-convect-range-with-5-elements-instaview-and-flex-control-knobs-stainless-steel/J7G5658LSF"><u>LG</u></a> and GE — though it should be noted the GE Cafe series can get up to $5,000. </p><p>The Charlie is not Energy Star-certified, so you can’t get any government rebates from that program, but there are several other local clean energy tax credits that can help ease the cost.</p><p>However, it did get the seal of approval from the New York City Housing Authority, which is installing <a href="https://www.nytimes.com/2026/01/16/nyregion/induction-stoves-housing-authority.html"><u>100 Copper Charlie stoves in apartments</u></a> throughout the city as a pilot program for lower-income residents, as all city-owned apartment buildings get converted from gas to electric.</p><p>“There are so many families that haven't had the ability to cook for years because they had their utility to get the gas shut off,” said Lisa Pickney, a professional chef who created the Official Dessert of the 1996 Atlanta Olympics, served on the Obama White House culinary team, and now works for Copper. </p><p>“And so they just gave them a hot plate and a rice cooker and a teapot. And it's just not right. They can't make a Thanksgiving turkey. So that kind of broke my heart. And now they'll be able to have Thanksgiving and do proper cooking now.” </p><p>The Copper Charlie isn’t the only induction stove of its kind; a rival company, the <a href="https://www.tomsguide.com/home/home-appliances/i-tried-out-an-induction-stove-that-works-even-when-the-power-goes-out"><u>Electra Induction Stove</u></a>, works in much the same way. Rather than the lithium ion batteries you might find in your phone or electric scooter, these stoves use a lithium phosphate battery, which is more stable, and more suited for how it’s used with the stoves. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OqREmX"></div>                            </div>                            <script src="https://kwizly.com/embed/OqREmX.js" async></script><h3 class="article-body__section" id="section-now-we-re-cooking"><span>Now we're cooking</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Cv4aYBK8AB99GeMZm3MFmU" name="Copper Charlie scallops" alt="searing scallops on the Copper Charlie" src="https://cdn.mos.cms.futurecdn.net/Cv4aYBK8AB99GeMZm3MFmU.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>As I (well, mostly Lisa) were cooking, I was impressed with how fast the oven heated up. While I didn’t time it, the Copper Charlie seemed far shorter than my oven at home. Similarly, its cooktop element warmed up the pan in no time, and put a sear on the scallops as good as I’ve ever done on any range, gas or electric.</p><p>Booth said that his cooking game has improved since getting the Charlie. “I like to make a Spanish potato omelet,” he said. “I like it because you can get a really nice, low temperature, so it doesn't brown too quickly. With the gas, I could get a low flame, but then the heat doesn't distribute quite as well, and so it's like cooking more in one spot and less on the edges.” </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:57.65%;"><img id="7in93fN4mpP8rnadB7BmuY" name="Copper Charlie chicken" alt="Chicken baked in the Copper Charlie" src="https://cdn.mos.cms.futurecdn.net/7in93fN4mpP8rnadB7BmuY.jpg" mos="" align="middle" fullscreen="" width="2000" height="1153" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Booth said he could no longer do the <a href="https://bittmanproject.com/recipe/seared-steak/" target="_blank">Mark Bittman trick of cooking a steak</a> by placing a pan on the very bottom of the oven, but the Charlie proved its worth when the outlet behind the stove stopped working. </p><p>“At first, I was like, what are we gonna do?” he said. “It's gonna take a while to get an electrician to come, and then I was like, oh, no, wait, it's fine. We have an outlet behind that cluster of little appliances in the corner is a four-prong outlet with a GFCI. So I just plugged it into that.”</p><p>“That to me really proved to me how much power this draws, because we've got an Instapot, we've got a kettle, we've got an espresso machine.  And we never had an issue, it never popped the GFCI.”</p><h3 class="article-body__section" id="section-how-it-s-working-out"><span>How it's working out</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="js4rQ6VnrMbiGjMoaYsBZ4" name="Copper Charlie induction range" alt="Copper Charlie induction range" src="https://cdn.mos.cms.futurecdn.net/js4rQ6VnrMbiGjMoaYsBZ4.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Ultimately, eleven owners in Booth’s building ended up getting a Copper Charlie. It proved to be more expensive than those who merely opted to replace their gas line, but Booth said it was worth the expense.</p><p>“We're gonna spend all this money for these pipes in the wall, and all I get is to get a blue flame?” he asks. ”I want something better than that.”</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/I-tried-an-induction-cooktop-3-things-i-learned">I used an induction cooktop for the first time — here are 3 things I learned</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/mistakes-i-made-when-i-switched-to-an-induction-cooktop-and-how-to-avoid-them">5 mistakes I made when I switched to an induction cooktop</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/we-tested-3-portable-induction-cooktops-at-wildly-different-prices-heres-our-verdict">We tested 3 portable induction cooktops from $50 to $1,500 </a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I'm an ex-Starbucks barista — you can make the Iced Brown Sugar Oatmilk Shaken Espresso at home for a fraction of the cost ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the eighteenth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>Starbucks is expensive. There's no getting around that. Some drinks can cost as much as $10 in certain states these days, which is simply ridiculous. If I were to order a <a href="https://www.starbucks.com/menu" target="_blank" rel="nofollow">Grande Iced Brown Sugar Oatmilk Shaken Espresso at the Starbucks closest to Tom's Guide's Manhattan office</a>, it would cost me $7.57. Yes, almost 8 bucks.  </p><p>Fortunately, you can make it yourself, for far less. </p><p>I was a Starbucks barista for years, and learned how to make the delicious Starbucks Iced Brown Sugar Oatmilk Shaken Espresso at home. I've even done some coffee math down below to show you how much better this version is for your wallet (and your health, if you use homemade syrup). </p><p>You only need three ingredients (four if you count ice as an ingredient, and five if you include the optional cinnamon) to make a delicious iced espresso at home. And the best part? It takes about two minutes. Here's my step-by-step guide to making the Starbucks Iced Brown Sugar Oatmilk Shaken Espresso at home, using the <a href="https://www.tomsguide.com/home/coffee-makers/nespresso-vertuo-up-review">Nespresso Vertuo Up</a>, Oatly Barista oat milk, homemade brown sugar syrup, ice, and cinnamon.</p><section class="howto-block">                    <h3>Step 1: Make your brown sugar syrup</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/LcZBZmCGRqRiaXyzQ9iQHi.png"                                        alt="a photo of a copycat starbucks iced brown sugar oatmilk shaken espresso"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/LcZBZmCGRqRiaXyzQ9iQHi.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                    <p><p>I opted to make my own syrup for two reasons. One: brown sugar syrup isn't readily available where I live, and two: I know the exact ingredients, so no surprises here. </p><p>Simply add your desired amount of sugar and a tiny drop of hot water to melt the sugar. If you have a sweet tooth, this might be 10g. I used one teaspoon, so 5g. </p></p>                </section><section class="howto-block">                    <h3>Step 2: Make your espresso</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/4MKv3iawqik6msXULAPCJi.png"                                        alt="a photo of a copycat starbucks iced brown sugar oatmilk shaken espresso"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/4MKv3iawqik6msXULAPCJi.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                    <p><p>Starbucks uses Blonde roast for the Iced Brown Sugar Oatmilk Shaken Espresso, so I'd recommend using a light or medium roast coffee with notes like citrus, cherry, caramel, or fruit to achieve a similar result. </p><p>Of course, you could simply buy Blonde roast from your local Starbucks. </p><p>I'd recommend a double shot here, so use around 18g of finely ground coffee for 36g espresso. I used the Oatly Nespresso pods for this video using my Nespresso Vertuo Up.</p></p>                </section><section class="howto-block">                    <h3>Step 3: Combine your ingredients in a shaker</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/fXSWB3dSTwhQ4VEZrWuNbi.png"                                        alt="a photo of a copycat starbucks iced brown sugar oatmilk shaken espresso"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/fXSWB3dSTwhQ4VEZrWuNbi.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                    <p><p>If you don't have a shaker, you could use a water bottle or even a leakproof Tupperware. </p><p>Add your espresso, syrup, ice, a splash of oat milk, and optional cinnamon if you like it a little warming. </p><p>Shake, pour, and you're done!</p></p>                </section>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7668471634939874573" data-video-id="7668471634939874573" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ electric piano bossa nova jazz(1392318) - Kenta Fukuoka" href="https://www.tiktok.com/music/electric-piano-bossa-nova-jazz-1392318-7211216288398444546">♬ electric piano bossa nova jazz(1392318) - Kenta Fukuoka</a></section>                    </blockquote></div>                <h2 id="the-coffee-math">The coffee math</h2><p>I hate math, but I love coffee math. Let's see if making your own Starbucks Iced Brown Sugar Oatmilk Shaken Espresso actually is cheaper — and how many times you have to make it yourself to break even.</p><h2 class="article-body__section" id="section-with-a-nespresso-machine"><span>With a Nespresso machine</span></h2><ol start="1"><li><strong>Nespresso Vertuo Pop+:</strong><a href="https://www.amazon.com/Nespresso-Espresso-Machine-DeLonghi-Liquorice/dp/B0DVD67F76" target="_blank" rel="nofollow"> $130 from Amazon</a></li><li><strong>Oat milk:</strong> <a href="https://www.walmart.com/ip/Planet-Oat-Oat-Milk-Velvety-Smooth-Barista-Lovers-52-fl-oz/5040429010" target="_blank" rel="nofollow">$3.32/52 fluid ounces from Walmart | 0.19 per drink</a></li><li><strong>Brown sugar:</strong> <a href="https://www.walmart.com/ip/Great-Value-Light-Brown-Sugar-32-oz/10315012" target="_blank" rel="nofollow">$2.02/32 ounces from Walmart | 0.01 per drink</a></li><li><strong>Coffee: </strong><a href="https://www.walmart.com/ip/Starbucks-By-Nespresso-Vertuo-Line-Starbucks-Blonde-Espresso-Roast-Nespresso-Pods-10-ct/348136846" target="_blank" rel="nofollow">Starbucks Blonde Nespresso Vertuo pods: $12.97 for 10 | 1.30 per pod</a></li></ol><p>So that's <strong>$1.50</strong> for the ingredients, $130 for the machine. </p><p>You’d need to drink 22 drinks to start making a profit. After that, it’s <strong>$6.07</strong> in savings per drink.</p><h2 class="article-body__section" id="section-with-an-espresso-machine-grinder"><span>With an espresso machine + grinder</span></h2><ol start="1"><li><strong>Breville Bambino:</strong><a href="https://www.amazon.com/Breville-Bambino-Espresso-Machine-Stainless/dp/B0B1JPPG2L" target="_blank" rel="nofollow"><strong> </strong>$299 from Amazon </a></li><li><strong>Baratza Encore ESP:</strong><a href="https://www.amazon.com/Baratza-Encore-Electric-Coffee-Grinder/dp/B0BW272XCV" target="_blank" rel="nofollow"> $199 from Amazon</a></li><li><strong>Starbucks Blonde whole bean coffee:</strong><a href="https://www.walmart.com/ip/Starbucks-Whole-Bean-Blonde-Espresso-Roast-Coffee-18-oz/17845554263" target="_blank" rel="nofollow"><strong> </strong>$17.13/18 ounces | $0.57 per drink</a></li><li><strong>Oat milk:</strong> <a href="https://www.walmart.com/ip/Planet-Oat-Oat-Milk-Velvety-Smooth-Barista-Lovers-52-fl-oz/5040429010" target="_blank" rel="nofollow">$3.32/52 fluid ounces from Walmart | 0.19 per drink</a></li><li><strong>Brown sugar:</strong> <a href="https://www.walmart.com/ip/Great-Value-Light-Brown-Sugar-32-oz/10315012" target="_blank" rel="nofollow">$2.02/32 ounces from Walmart | 0.01 per drink</a></li></ol><p>This version has a higher initial cost but cheaper overall ingredients: <strong>$0.77</strong> for the ingredients, $498 for the machines.</p><p>You'd need to drink 74 drinks to start making a profit. After that, it’s <strong>$6.80</strong> of pure savings each time.</p><p>If you already have an espresso machine, you're saving almost <strong>90%</strong> on every drink. </p><p>So, are you going to skip Starbucks and try making the Iced Brown Sugar Oatmilk Shaken Espresso at home? I only ever make it myself — I haven't been into a Starbucks in years. </p><p>Are there any other Starbucks drinks you'd like me to simplify? Perhaps the Pumpkin Spiced Latte in September? Or the Eggnog latte in December? Let me know in the comments!</p><p>I post new episodes of The Coffee Lab every Friday, so be sure to check back next week.</p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-the-new-nespresso-summer-flavors-heres-my-favorite"><strong>I tried the new Nespresso summer flavors — here's my favorite</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/non-plastic-coffee-makers-are-trending-and-this-low-plastic-ratio-model-is-my-favorite-for-coffee-snobs"><strong>Non-plastic coffee makers are trending — and this low-plastic Ratio model is my favorite for coffee snobs</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/im-an-ex-starbucks-barista-you-can-make-the-iced-brown-sugar-oatmilk-shaken-espresso-at-home-for-a-fraction-of-the-cost</link>
                                                                            <description>
                            <![CDATA[ For the eighteenth episode of The Coffee Lab, I let you in on a little Starbucks secret and show you how to make one of my favorite summer drinks. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">hr5zsoC8LGSQvyLoVz7YiX</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/d9pDVVLQk5w4wu2EegDg49-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Fri, 31 Jul 2026 08:00:00 +0000</pubDate>                                                                                                                                <updated>Fri, 31 Jul 2026 14:53:28 +0000</updated>
                                                                                                                                            <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/png" url="https://cdn.mos.cms.futurecdn.net/d9pDVVLQk5w4wu2EegDg49-1280-80.png">
                                                            <media:credit><![CDATA[Erin Bashford]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[ingredients for the starbucks iced brown sugar oat milk espresso ]]></media:description>                                                            <media:text><![CDATA[ingredients for the starbucks iced brown sugar oat milk espresso ]]></media:text>
                                <media:title type="plain"><![CDATA[ingredients for the starbucks iced brown sugar oat milk espresso ]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/d9pDVVLQk5w4wu2EegDg49-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the eighteenth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>Starbucks is expensive. There's no getting around that. Some drinks can cost as much as $10 in certain states these days, which is simply ridiculous. If I were to order a <a href="https://www.starbucks.com/menu" target="_blank" rel="nofollow">Grande Iced Brown Sugar Oatmilk Shaken Espresso at the Starbucks closest to Tom's Guide's Manhattan office</a>, it would cost me $7.57. Yes, almost 8 bucks.  </p><p>Fortunately, you can make it yourself, for far less. </p><p>I was a Starbucks barista for years, and learned how to make the delicious Starbucks Iced Brown Sugar Oatmilk Shaken Espresso at home. I've even done some coffee math down below to show you how much better this version is for your wallet (and your health, if you use homemade syrup). </p><p>You only need three ingredients (four if you count ice as an ingredient, and five if you include the optional cinnamon) to make a delicious iced espresso at home. And the best part? It takes about two minutes. Here's my step-by-step guide to making the Starbucks Iced Brown Sugar Oatmilk Shaken Espresso at home, using the <a href="https://www.tomsguide.com/home/coffee-makers/nespresso-vertuo-up-review">Nespresso Vertuo Up</a>, Oatly Barista oat milk, homemade brown sugar syrup, ice, and cinnamon.</p><section class="howto-block">                    <h3>Step 1: Make your brown sugar syrup</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/LcZBZmCGRqRiaXyzQ9iQHi.png"                                        alt="a photo of a copycat starbucks iced brown sugar oatmilk shaken espresso"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/LcZBZmCGRqRiaXyzQ9iQHi.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                    <p><p>I opted to make my own syrup for two reasons. One: brown sugar syrup isn't readily available where I live, and two: I know the exact ingredients, so no surprises here. </p><p>Simply add your desired amount of sugar and a tiny drop of hot water to melt the sugar. If you have a sweet tooth, this might be 10g. I used one teaspoon, so 5g. </p></p>                </section><section class="howto-block">                    <h3>Step 2: Make your espresso</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/4MKv3iawqik6msXULAPCJi.png"                                        alt="a photo of a copycat starbucks iced brown sugar oatmilk shaken espresso"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/4MKv3iawqik6msXULAPCJi.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                    <p><p>Starbucks uses Blonde roast for the Iced Brown Sugar Oatmilk Shaken Espresso, so I'd recommend using a light or medium roast coffee with notes like citrus, cherry, caramel, or fruit to achieve a similar result. </p><p>Of course, you could simply buy Blonde roast from your local Starbucks. </p><p>I'd recommend a double shot here, so use around 18g of finely ground coffee for 36g espresso. I used the Oatly Nespresso pods for this video using my Nespresso Vertuo Up.</p></p>                </section><section class="howto-block">                    <h3>Step 3: Combine your ingredients in a shaker</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/fXSWB3dSTwhQ4VEZrWuNbi.png"                                        alt="a photo of a copycat starbucks iced brown sugar oatmilk shaken espresso"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/fXSWB3dSTwhQ4VEZrWuNbi.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Erin Bashford)</div></figure>                    <p><p>If you don't have a shaker, you could use a water bottle or even a leakproof Tupperware. </p><p>Add your espresso, syrup, ice, a splash of oat milk, and optional cinnamon if you like it a little warming. </p><p>Shake, pour, and you're done!</p></p>                </section>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7668471634939874573" data-video-id="7668471634939874573" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ electric piano bossa nova jazz(1392318) - Kenta Fukuoka" href="https://www.tiktok.com/music/electric-piano-bossa-nova-jazz-1392318-7211216288398444546">♬ electric piano bossa nova jazz(1392318) - Kenta Fukuoka</a></section>                    </blockquote></div>                <h2 id="the-coffee-math">The coffee math</h2><p>I hate math, but I love coffee math. Let's see if making your own Starbucks Iced Brown Sugar Oatmilk Shaken Espresso actually is cheaper — and how many times you have to make it yourself to break even.</p><h2 class="article-body__section" id="section-with-a-nespresso-machine"><span>With a Nespresso machine</span></h2><ol start="1"><li><strong>Nespresso Vertuo Pop+:</strong><a href="https://www.amazon.com/Nespresso-Espresso-Machine-DeLonghi-Liquorice/dp/B0DVD67F76" target="_blank" rel="nofollow"> $130 from Amazon</a></li><li><strong>Oat milk:</strong> <a href="https://www.walmart.com/ip/Planet-Oat-Oat-Milk-Velvety-Smooth-Barista-Lovers-52-fl-oz/5040429010" target="_blank" rel="nofollow">$3.32/52 fluid ounces from Walmart | 0.19 per drink</a></li><li><strong>Brown sugar:</strong> <a href="https://www.walmart.com/ip/Great-Value-Light-Brown-Sugar-32-oz/10315012" target="_blank" rel="nofollow">$2.02/32 ounces from Walmart | 0.01 per drink</a></li><li><strong>Coffee: </strong><a href="https://www.walmart.com/ip/Starbucks-By-Nespresso-Vertuo-Line-Starbucks-Blonde-Espresso-Roast-Nespresso-Pods-10-ct/348136846" target="_blank" rel="nofollow">Starbucks Blonde Nespresso Vertuo pods: $12.97 for 10 | 1.30 per pod</a></li></ol><p>So that's <strong>$1.50</strong> for the ingredients, $130 for the machine. </p><p>You’d need to drink 22 drinks to start making a profit. After that, it’s <strong>$6.07</strong> in savings per drink.</p><h2 class="article-body__section" id="section-with-an-espresso-machine-grinder"><span>With an espresso machine + grinder</span></h2><ol start="1"><li><strong>Breville Bambino:</strong><a href="https://www.amazon.com/Breville-Bambino-Espresso-Machine-Stainless/dp/B0B1JPPG2L" target="_blank" rel="nofollow"><strong> </strong>$299 from Amazon </a></li><li><strong>Baratza Encore ESP:</strong><a href="https://www.amazon.com/Baratza-Encore-Electric-Coffee-Grinder/dp/B0BW272XCV" target="_blank" rel="nofollow"> $199 from Amazon</a></li><li><strong>Starbucks Blonde whole bean coffee:</strong><a href="https://www.walmart.com/ip/Starbucks-Whole-Bean-Blonde-Espresso-Roast-Coffee-18-oz/17845554263" target="_blank" rel="nofollow"><strong> </strong>$17.13/18 ounces | $0.57 per drink</a></li><li><strong>Oat milk:</strong> <a href="https://www.walmart.com/ip/Planet-Oat-Oat-Milk-Velvety-Smooth-Barista-Lovers-52-fl-oz/5040429010" target="_blank" rel="nofollow">$3.32/52 fluid ounces from Walmart | 0.19 per drink</a></li><li><strong>Brown sugar:</strong> <a href="https://www.walmart.com/ip/Great-Value-Light-Brown-Sugar-32-oz/10315012" target="_blank" rel="nofollow">$2.02/32 ounces from Walmart | 0.01 per drink</a></li></ol><p>This version has a higher initial cost but cheaper overall ingredients: <strong>$0.77</strong> for the ingredients, $498 for the machines.</p><p>You'd need to drink 74 drinks to start making a profit. After that, it’s <strong>$6.80</strong> of pure savings each time.</p><p>If you already have an espresso machine, you're saving almost <strong>90%</strong> on every drink. </p><p>So, are you going to skip Starbucks and try making the Iced Brown Sugar Oatmilk Shaken Espresso at home? I only ever make it myself — I haven't been into a Starbucks in years. </p><p>Are there any other Starbucks drinks you'd like me to simplify? Perhaps the Pumpkin Spiced Latte in September? Or the Eggnog latte in December? Let me know in the comments!</p><p>I post new episodes of The Coffee Lab every Friday, so be sure to check back next week.</p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-the-new-nespresso-summer-flavors-heres-my-favorite"><strong>I tried the new Nespresso summer flavors — here's my favorite</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/non-plastic-coffee-makers-are-trending-and-this-low-plastic-ratio-model-is-my-favorite-for-coffee-snobs"><strong>Non-plastic coffee makers are trending — and this low-plastic Ratio model is my favorite for coffee snobs</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ HelloFresh Australia review: flexible, fuss-free meal options, but not always the best bang for your buck ]]></title>
                                                                                                <dc:content><![CDATA[ <p>As much as I consider myself a whiz in the kitchen, for me, there's nothing worse than having to think about what's for dinner every night. I'm frequently indecisive, can't commit to meal prep for fear of taste bud boredom, and I would much rather scroll through DoorDash than try to piece together a meal from ingredients in my pantry. </p><p>When the opportunity arose to try out HelloFresh Australia for a month, I was ecstatic. I'm no stranger to HelloFresh (and its subsidiaries, YouFoodz and EveryPlate), as it consistently ranks as one of the best meal kit delivery services globally. It takes the hassle out of dinnertime by menu planning and food shopping for you. The service handpicks ingredients, organises timely deliveries and offers you meals from an expansive catalogue of weekly recipes. </p><p>To put HelloFresh to the test, I whipped out my chef hat, dusted off my somewhat dormant cooking skills and selected a variety of recipes for my two-person household. Do note, though, HelloFresh expects that subscribers have some culinary staples and cooking experience in the kitchen to get the most out of it. That said, if you've cut a carrot or minced some garlic before, you've surely got enough skill for this beginner-friendly meal kit. </p><p>I'll stop with the waffling now — here's how I fared during my monthlong review of HelloFresh AU. </p><h3 class="article-body__section" id="section-hellofresh-australia-review-price-and-availability"><span>HelloFresh Australia review: price and availability </span></h3><p>Much like other meal-kit providers, prices for HelloFresh will differ depending on how many meals per week you order and how many people you need to feed. The total per-serve cost typically goes down when you opt for more meals. </p><p>HelloFresh offers meal kit subscriptions for two to six people, with options of two to six recipes. I purchased six meals for two people to get the most out of HelloFresh's huge weekly menu. It cost AU$136.50 plus shipping per week, which worked out to AU$11.37 per person per serve. </p><p>You can add bonus meals to your plan for an additional out-of-pocket cost. For example, one week, I opted to include another meal on top of our six recipe plan. It worked out to AU$157.68 or AU$11.26 per person, per serve. It's not the biggest saving — 11 cents per serve — but if you had more mouths to feed, the cost per serving would be significantly less. As per HelloFresh, the cost per serve for a four-person, six-meal week is AU$9.29 (or AU$223 total) and the cost per serve for a six-person, six-meal week is AU$8.83 (or AU$318 total). </p><p>You can also add one-off desserts, sides, snacks and family-style catering meals to your plan for an additional fee. HelloFresh owns YouFoodz, a convenience-based meal subscription service, so there were a few ready meal options to choose from in the weekly menu as well. </p><p>When compared to the cost of groceries, HelloFresh costs slightly less than our usual AU$200 two-person weekly shop, and it is definitely cheaper per serve than getting our local Chinese takeaway, which is AU$35 for our order. We still had some change leftover to nab our weekly essentials, lunch and snacks. </p><p>That said, larger families might get more bang for their buck by heading to the supermarket, as there are more meals than dinner to consider. According to Finder, the <a href="https://www.finder.com.au/budgeting/average-grocery-bill#average-cost-of-groceries-by-household-size" target="_blank" rel="nofollow">average cost of groceries for a family of four is AU$274 per week</a>, so the AU$223 HelloFresh box cost that only covers dinnertime meals might not provide the best value for money. </p><p>It is also worth noting that HelloFresh frequently provides discount codes and free delivery for new subscribers. These discounts usually knock off savings over the course of four deliveries, making prices more affordable to try out for a short period.</p><h3 class="article-body__section" id="section-hellofresh-australia-review-delivery-and-customer-service"><span>HelloFresh Australia review: Delivery and customer service</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4097px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="7MMivnCL5gTnPvaUvNKWok" name="hellofresh-1" alt="HelloFresh Box" src="https://cdn.mos.cms.futurecdn.net/7MMivnCL5gTnPvaUvNKWok.jpg" mos="" align="middle" fullscreen="" width="4097" height="2305" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>Delivery for HelloFresh is a rather seamless process. Deliveries can occur on any given day during the week, depending on your area, and are conducted via a third-party service. You also don't need to be home to accept the delivery, as the service will leave your box in a safe location. HelloFresh delivers to most metro, regional and rural suburbs around mainland Australia, but it doesn't currently offer delivery to Tasmania. </p><p>In my case, I could organise delivery for either Wednesday or Sunday, during two timeframes of either early morning (between 12am and 4am) or during the day (between 8am and 12pm). For greater context, my colleague in a neighbouring suburb could only select the early morning timeslot on a Sunday, so I’d highly recommend researching delivery days and times available in your area before you commit to HelloFresh. Additionally, I paid AU$11.99 on top of my boxes for delivery. The fee was initially waived for my first two boxes, as part of a free delivery promotion. </p><p>Subscribers can also adjust or skip weekly orders any time up until five days before the expected delivery date. This is particularly useful if you’re away from home for any period of time and simply want to hit pause on your HelloFresh subscription.</p><p>In the highly unlikely event that anything happens to your subscription box — such as it arrives with out-of-date ingredients or missing items — you can contact HelloFresh's customer support team. It is worth noting that you won't necessarily receive a replacement box or ingredient; however, the team will likely give you a credit or refund should anything go awry. </p><h3 class="article-body__section" id="section-hellofresh-australia-review-meals"><span>HelloFresh Australia review: Meals</span></h3><p>HelloFresh Australia offers over 45 fresh recipes to choose from in its weekly menu and it varies week to week. Meals fall into different menu categories to cater to dietary needs and preferences. These include: Classic, Vegetarian, Family-friendly, Calorie Smart, Quick & Easy, Carb Smart, Flexitarian and Protein Rich. You can also opt for air-fryer-friendly options or dinners that will take 30 minutes or less to prepare for the ultimate convenience factor. </p><p>I selected meals from the Carb Smart and Calorie Smart menus, which feature a variety of cuisines and cater to those who want more balanced dinners with lower carbohydrates and calories. I also selected some heat-and-eat options (more on that below) for late nights when I craved a lazy, decision-free dinner. </p><a href="https://www.hellofresh.com.au/"><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3054px;"><p class="vanilla-image-block" style="padding-top:49.18%;"><img id="hZCNBgndG962v7wBhFP8za" name="Screenshot 2026-05-11 at 12.43.10 pm" alt="Screenshot of HelloFresh AU menu" src="https://cdn.mos.cms.futurecdn.net/hZCNBgndG962v7wBhFP8za.png" mos="" align="middle" fullscreen="" width="3054" height="1502" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">An example of my preselected meals during my HelloFresh review </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure></a><p>Your menu each week is automatically populated with preselected meals that HelloFresh recommends according to your chosen menu and previous orders. However, you have free rein to chop and change meals as you see fit by unselecting them and choosing another option from the recipes on offer. </p><p>You shouldn’t expect an entirely fresh menu each week, as HelloFresh will reuse the same dishes week to week. For example, there were some repeat preselected meals in the final week of my subscription, but I simply opted not to include them. For the most part, there were few, if any, duplicate recipes each week, which was ideal for my foodie taste buds. </p><p>I gravitated towards Asian-inspired dishes, quick dinners and easy, but delicious protein-based recipes throughout my review. Out of the 26 recipes I made, my favourites included Chermoula Honey Roast Lamb, Salt'n'Pepper Salmon and Lemony Greens, Homestyle Chicken Schnitzel and Mustard Mayo and Singaporean Prawn and Noodle Stir Fry. </p><p>It’s also worth mentioning that HelloFresh does an excellent job with clearly listing allergen information for its recipes, with details on its website and app for each recipe. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3222px;"><p class="vanilla-image-block" style="padding-top:53.63%;"><img id="4xkEDSNZGripqCcFgn8njZ" name="Screenshot 2026-05-11 at 12.43.32 pm" alt="Screenshot of HelloFresh AU menu" src="https://cdn.mos.cms.futurecdn.net/4xkEDSNZGripqCcFgn8njZ.png" mos="" align="middle" fullscreen="" width="3222" height="1728" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>On the subject of allergens, when you browse the menu on either the website or app, not only can you filter dishes by cuisine, but you can select ingredient types to not include. The only slight issue I found with this is that nuts, for example, is listed as a singular category. If you’re allergic to peanuts, say, but can eat cashews and almonds, you can’t filter to such a granular level.</p><p>You can also swap out the default protein in the meals for something else, if you'd prefer. For our chosen menu, our preselected options usually included chicken, so we opted to swap for pork or salmon a few times. You can also swap chicken thigh for chicken tenderloins or breast, for example, if you prefer white meat. </p><h3 class="article-body__section" id="section-hellofresh-australia-review-recipes-and-ingredients"><span>HelloFresh Australia review: Recipes and ingredients</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5210px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="apFd732QqyUzzChJW5ZMzm" name="hellofresh-5" alt="Hellofresh box with recipe cards" src="https://cdn.mos.cms.futurecdn.net/apFd732QqyUzzChJW5ZMzm.jpg" mos="" align="middle" fullscreen="" width="5210" height="2931" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>Inside the box, HelloFresh ingredients are packaged in brown paper bags with a corresponding number for each recipe card. The cold ingredients — i.e. meat and dairy products — are stored separately in an insulated cold bag with ice packs. This bag isn’t sealed, which I found a little disappointing, because if you’re able to receive the box for some time and it’s left outside, it could be more susceptible to warming up. The box also includes some discount codes for friends and family, as well as discounts for other businesses supported by HelloFresh. All the ingredients I received came fresh with no issues and didn't go to waste. </p><p>Each bag provides most — but not all — ingredients for the recipe. You'll need to have some basic kitchen staples on hand, like butter, oil, salt and pepper. You'll also need utensils, like sharp knives, pots, pans and cooking utensils to make each dish. For most recipes, I had everything I needed, but I had to purchase white wine vinegar before making two meals. You can also check the recipes online to make sure you have any extra ingredients ready to go. </p><p>The recipe cards are pretty straightforward and easy to follow, with some even offering handy kitchen tips. There's a handy picture guide that walks you through step-by-step, and there's even a QR code so you can read the recipe online if you'd prefer. And, while it didn’t apply to my household, I appreciated HelloFresh offering tips aimed at children who may want to get involved with the cooking process.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3432px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="umKajccZk7gxbQAqH393Wk" name="hellofresh-7" alt="Hellofresh salad" src="https://cdn.mos.cms.futurecdn.net/umKajccZk7gxbQAqH393Wk.jpg" mos="" align="middle" fullscreen="" width="3432" height="1930" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>Throughout my review, I found estimated prep times to be accurate, but you might need to budget more time if you're new to cooking or specific techniques. There was only one instance where I felt it was a bit off. For example, when roasting lamb for the Chermoula Honey Roast Lamb recipe, I was instructed to sear the lamb rump for 12 minutes in a pan before transferring it to the oven for an additional 25 minutes. </p><p>At the time, I had not roasted or seared lamb before, so I had little experience of what it should look like. It took me a while longer — around 16 minutes to full sear — pushing out the prep time to around 57 minutes, instead of the budgeted 40 minutes. Despite this minor hiccup, the meal turned out perfectly. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3006px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CwLF555Q3oveEmNivhwe8k" name="hellofresh-6" alt="HelloFresh meal preparation" src="https://cdn.mos.cms.futurecdn.net/CwLF555Q3oveEmNivhwe8k.jpg" mos="" align="middle" fullscreen="" width="3006" height="1691" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>Another meal we particularly enjoyed was the Crumbed Mexi-Spiced Chicken & Corn Tacos, which came with a small bottle of Cholula hot sauce. This meal was made easy by using an air fryer to cook the chicken, and came with a can of sweet corn for ease. If you don't own an air fryer, the recipe also provides details on how to pan-fry the chicken. </p><p>The recipe gave me great advice on how to crumb the chicken without making too much of a mess — by separating the crumbs, spices and egg into separate bowls — and how to chargrill the corn with a pan without burning it. This recipe budgeted 25 minutes from start to finish, and it was pretty accurate. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/4Cp74LdyduMHCbcQ9Dhj5m.jpg" alt="Hand holding Hellofresh meal" /><figcaption>An example of HelloFresh AU's ready meal selection<small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/L2kVTUPyatyXj4VQtvZrJn.jpg" alt="Hellofresh ready meal plated" /><figcaption>HelloFresh's ready meal plated<small role="credit">Future / Lucy Scotting</small></figcaption></figure></figure><p>Further, I did opt to include some ready meal pasta dishes to our weekly boxes and it was very interesting to say the least. As someone who had tried YouFoodz before, I was familiar with some of the recipes, and the options that HelloFresh had to offer were very much in line with the previous YouFoodz I've had. I did use these for a lazy dinner on a few evenings, and the instructions were as simple as expected — peel back the lid and pop it in the microwave for 3 minutes on high. Flavour-wise, the meals were pretty good and satisfied my hunger, but they definitely weren't Michelin-rated. </p><p>The only real gripe we had was flavour and portion sizes. We're self-proclaimed foodies, and love trying new dishes, so we were thrilled to have such a range of cuisines to choose from. However, we found we need to add more than just the provided spice mixes to enhance dishes. The flavour depth was slightly lacking, which might disappoint those who prefer bold, heavy seasoning. </p><p>The other niggle we had was portion sizes. As you can imagine, being foodies means we like to eat and some of our protein portions didn't quite sustain us. In some cases, we ended up making two meals and eating them family style to feel fulfilled. Now, HelloFresh suggests opting for a larger meal kit size if you're worried about this, but you can always upgrade or downgrade your plan at any time. There are some recipes that come with 'premium' protein ingredients for an additional fee, too, with larger meat/protein allocations, but we didn't feel the need to pay for these recipes. </p><h3 class="article-body__section" id="section-hellofresh-australia-review-verdict"><span>HelloFresh Australia review: Verdict</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3389px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="N3SiNJESmjapAnSDPEd9Lk" name="hellofresh-8" alt="HelloFresh meals on plates" src="https://cdn.mos.cms.futurecdn.net/N3SiNJESmjapAnSDPEd9Lk.jpg" mos="" align="middle" fullscreen="" width="3389" height="1906" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>HelloFresh does exactly what you expect — takes the stress out of figuring out weekly dinners and offers the convenience of handpicked ingredients. I still had plenty of choice in what meals we got in every week, but it took immense pressure off hitting the shops after a long workday. Plus, with constant coupons offering savings for new subscribers, you could spend less than you would on takeaway every week. </p><p>Aside from that, the delivery was pretty seamless and I had no issue with editing meals or my order at any time in the lead-up to my next box. That said, I did spend some extra time prepping than allocated in some recipe cards, and portion sizes felt small, but overall, HelloFresh's meal kits didn't disappoint. And now with a more expanded range that includes air fryer-friendly meals and heat-and-eat dinners, there's much more accessibility on offer with HelloFresh's service. </p><p>So if you're looking for a simple and easy alternative to indecisive dinners, HelloFresh is a subscription box to consider.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/homes/hellofresh-australia-review</link>
                                                                            <description>
                            <![CDATA[ Is HelloFresh Australia worth it in 2026? I put the meal kit subscription service to the test for value, variety and prep time. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">MxLqFrQts68YNtPazRBsN7</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/sEosoxQQmBfBczP4hYRRfj-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 31 Jul 2026 02:14:37 +0000</pubDate>                                                                                                                                <updated>Fri, 31 Jul 2026 02:16:36 +0000</updated>
                                                                                                                                            <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ lucy.scotting@futurenet.com (Lucy Scotting) ]]></author>                    <dc:creator><![CDATA[ Lucy Scotting ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/UC9ncAYxkmJ5ipHEyX44ri.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Lucy Scotting is a staff writer for Tom’s Guide Australia, primarily covering lifestyle, streaming and internet-related news. Lucy started her career writing for HR and staffing industry publications, with articles covering emerging tech, business and finance.&lt;/p&gt;&lt;p&gt;In her spare time, Lucy can be found watching sci-fi movies, working on her dystopian fiction novel (in progress since 2017) or hanging out with her dog, Fletcher. If she’s not found doing any of the above, she’s likely on her next adventure to a new city, country, or continent. &lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/sEosoxQQmBfBczP4hYRRfj-1280-80.jpg">
                                                            <media:credit><![CDATA[HelloFresh]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Hellofresh box with vegetables]]></media:description>                                                            <media:text><![CDATA[Hellofresh box with vegetables]]></media:text>
                                <media:title type="plain"><![CDATA[Hellofresh box with vegetables]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/sEosoxQQmBfBczP4hYRRfj-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As much as I consider myself a whiz in the kitchen, for me, there's nothing worse than having to think about what's for dinner every night. I'm frequently indecisive, can't commit to meal prep for fear of taste bud boredom, and I would much rather scroll through DoorDash than try to piece together a meal from ingredients in my pantry. </p><p>When the opportunity arose to try out HelloFresh Australia for a month, I was ecstatic. I'm no stranger to HelloFresh (and its subsidiaries, YouFoodz and EveryPlate), as it consistently ranks as one of the best meal kit delivery services globally. It takes the hassle out of dinnertime by menu planning and food shopping for you. The service handpicks ingredients, organises timely deliveries and offers you meals from an expansive catalogue of weekly recipes. </p><p>To put HelloFresh to the test, I whipped out my chef hat, dusted off my somewhat dormant cooking skills and selected a variety of recipes for my two-person household. Do note, though, HelloFresh expects that subscribers have some culinary staples and cooking experience in the kitchen to get the most out of it. That said, if you've cut a carrot or minced some garlic before, you've surely got enough skill for this beginner-friendly meal kit. </p><p>I'll stop with the waffling now — here's how I fared during my monthlong review of HelloFresh AU. </p><h3 class="article-body__section" id="section-hellofresh-australia-review-price-and-availability"><span>HelloFresh Australia review: price and availability </span></h3><p>Much like other meal-kit providers, prices for HelloFresh will differ depending on how many meals per week you order and how many people you need to feed. The total per-serve cost typically goes down when you opt for more meals. </p><p>HelloFresh offers meal kit subscriptions for two to six people, with options of two to six recipes. I purchased six meals for two people to get the most out of HelloFresh's huge weekly menu. It cost AU$136.50 plus shipping per week, which worked out to AU$11.37 per person per serve. </p><p>You can add bonus meals to your plan for an additional out-of-pocket cost. For example, one week, I opted to include another meal on top of our six recipe plan. It worked out to AU$157.68 or AU$11.26 per person, per serve. It's not the biggest saving — 11 cents per serve — but if you had more mouths to feed, the cost per serving would be significantly less. As per HelloFresh, the cost per serve for a four-person, six-meal week is AU$9.29 (or AU$223 total) and the cost per serve for a six-person, six-meal week is AU$8.83 (or AU$318 total). </p><p>You can also add one-off desserts, sides, snacks and family-style catering meals to your plan for an additional fee. HelloFresh owns YouFoodz, a convenience-based meal subscription service, so there were a few ready meal options to choose from in the weekly menu as well. </p><p>When compared to the cost of groceries, HelloFresh costs slightly less than our usual AU$200 two-person weekly shop, and it is definitely cheaper per serve than getting our local Chinese takeaway, which is AU$35 for our order. We still had some change leftover to nab our weekly essentials, lunch and snacks. </p><p>That said, larger families might get more bang for their buck by heading to the supermarket, as there are more meals than dinner to consider. According to Finder, the <a href="https://www.finder.com.au/budgeting/average-grocery-bill#average-cost-of-groceries-by-household-size" target="_blank" rel="nofollow">average cost of groceries for a family of four is AU$274 per week</a>, so the AU$223 HelloFresh box cost that only covers dinnertime meals might not provide the best value for money. </p><p>It is also worth noting that HelloFresh frequently provides discount codes and free delivery for new subscribers. These discounts usually knock off savings over the course of four deliveries, making prices more affordable to try out for a short period.</p><h3 class="article-body__section" id="section-hellofresh-australia-review-delivery-and-customer-service"><span>HelloFresh Australia review: Delivery and customer service</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4097px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="7MMivnCL5gTnPvaUvNKWok" name="hellofresh-1" alt="HelloFresh Box" src="https://cdn.mos.cms.futurecdn.net/7MMivnCL5gTnPvaUvNKWok.jpg" mos="" align="middle" fullscreen="" width="4097" height="2305" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>Delivery for HelloFresh is a rather seamless process. Deliveries can occur on any given day during the week, depending on your area, and are conducted via a third-party service. You also don't need to be home to accept the delivery, as the service will leave your box in a safe location. HelloFresh delivers to most metro, regional and rural suburbs around mainland Australia, but it doesn't currently offer delivery to Tasmania. </p><p>In my case, I could organise delivery for either Wednesday or Sunday, during two timeframes of either early morning (between 12am and 4am) or during the day (between 8am and 12pm). For greater context, my colleague in a neighbouring suburb could only select the early morning timeslot on a Sunday, so I’d highly recommend researching delivery days and times available in your area before you commit to HelloFresh. Additionally, I paid AU$11.99 on top of my boxes for delivery. The fee was initially waived for my first two boxes, as part of a free delivery promotion. </p><p>Subscribers can also adjust or skip weekly orders any time up until five days before the expected delivery date. This is particularly useful if you’re away from home for any period of time and simply want to hit pause on your HelloFresh subscription.</p><p>In the highly unlikely event that anything happens to your subscription box — such as it arrives with out-of-date ingredients or missing items — you can contact HelloFresh's customer support team. It is worth noting that you won't necessarily receive a replacement box or ingredient; however, the team will likely give you a credit or refund should anything go awry. </p><h3 class="article-body__section" id="section-hellofresh-australia-review-meals"><span>HelloFresh Australia review: Meals</span></h3><p>HelloFresh Australia offers over 45 fresh recipes to choose from in its weekly menu and it varies week to week. Meals fall into different menu categories to cater to dietary needs and preferences. These include: Classic, Vegetarian, Family-friendly, Calorie Smart, Quick & Easy, Carb Smart, Flexitarian and Protein Rich. You can also opt for air-fryer-friendly options or dinners that will take 30 minutes or less to prepare for the ultimate convenience factor. </p><p>I selected meals from the Carb Smart and Calorie Smart menus, which feature a variety of cuisines and cater to those who want more balanced dinners with lower carbohydrates and calories. I also selected some heat-and-eat options (more on that below) for late nights when I craved a lazy, decision-free dinner. </p><a href="https://www.hellofresh.com.au/"><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3054px;"><p class="vanilla-image-block" style="padding-top:49.18%;"><img id="hZCNBgndG962v7wBhFP8za" name="Screenshot 2026-05-11 at 12.43.10 pm" alt="Screenshot of HelloFresh AU menu" src="https://cdn.mos.cms.futurecdn.net/hZCNBgndG962v7wBhFP8za.png" mos="" align="middle" fullscreen="" width="3054" height="1502" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">An example of my preselected meals during my HelloFresh review </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure></a><p>Your menu each week is automatically populated with preselected meals that HelloFresh recommends according to your chosen menu and previous orders. However, you have free rein to chop and change meals as you see fit by unselecting them and choosing another option from the recipes on offer. </p><p>You shouldn’t expect an entirely fresh menu each week, as HelloFresh will reuse the same dishes week to week. For example, there were some repeat preselected meals in the final week of my subscription, but I simply opted not to include them. For the most part, there were few, if any, duplicate recipes each week, which was ideal for my foodie taste buds. </p><p>I gravitated towards Asian-inspired dishes, quick dinners and easy, but delicious protein-based recipes throughout my review. Out of the 26 recipes I made, my favourites included Chermoula Honey Roast Lamb, Salt'n'Pepper Salmon and Lemony Greens, Homestyle Chicken Schnitzel and Mustard Mayo and Singaporean Prawn and Noodle Stir Fry. </p><p>It’s also worth mentioning that HelloFresh does an excellent job with clearly listing allergen information for its recipes, with details on its website and app for each recipe. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3222px;"><p class="vanilla-image-block" style="padding-top:53.63%;"><img id="4xkEDSNZGripqCcFgn8njZ" name="Screenshot 2026-05-11 at 12.43.32 pm" alt="Screenshot of HelloFresh AU menu" src="https://cdn.mos.cms.futurecdn.net/4xkEDSNZGripqCcFgn8njZ.png" mos="" align="middle" fullscreen="" width="3222" height="1728" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>On the subject of allergens, when you browse the menu on either the website or app, not only can you filter dishes by cuisine, but you can select ingredient types to not include. The only slight issue I found with this is that nuts, for example, is listed as a singular category. If you’re allergic to peanuts, say, but can eat cashews and almonds, you can’t filter to such a granular level.</p><p>You can also swap out the default protein in the meals for something else, if you'd prefer. For our chosen menu, our preselected options usually included chicken, so we opted to swap for pork or salmon a few times. You can also swap chicken thigh for chicken tenderloins or breast, for example, if you prefer white meat. </p><h3 class="article-body__section" id="section-hellofresh-australia-review-recipes-and-ingredients"><span>HelloFresh Australia review: Recipes and ingredients</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5210px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="apFd732QqyUzzChJW5ZMzm" name="hellofresh-5" alt="Hellofresh box with recipe cards" src="https://cdn.mos.cms.futurecdn.net/apFd732QqyUzzChJW5ZMzm.jpg" mos="" align="middle" fullscreen="" width="5210" height="2931" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>Inside the box, HelloFresh ingredients are packaged in brown paper bags with a corresponding number for each recipe card. The cold ingredients — i.e. meat and dairy products — are stored separately in an insulated cold bag with ice packs. This bag isn’t sealed, which I found a little disappointing, because if you’re able to receive the box for some time and it’s left outside, it could be more susceptible to warming up. The box also includes some discount codes for friends and family, as well as discounts for other businesses supported by HelloFresh. All the ingredients I received came fresh with no issues and didn't go to waste. </p><p>Each bag provides most — but not all — ingredients for the recipe. You'll need to have some basic kitchen staples on hand, like butter, oil, salt and pepper. You'll also need utensils, like sharp knives, pots, pans and cooking utensils to make each dish. For most recipes, I had everything I needed, but I had to purchase white wine vinegar before making two meals. You can also check the recipes online to make sure you have any extra ingredients ready to go. </p><p>The recipe cards are pretty straightforward and easy to follow, with some even offering handy kitchen tips. There's a handy picture guide that walks you through step-by-step, and there's even a QR code so you can read the recipe online if you'd prefer. And, while it didn’t apply to my household, I appreciated HelloFresh offering tips aimed at children who may want to get involved with the cooking process.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3432px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="umKajccZk7gxbQAqH393Wk" name="hellofresh-7" alt="Hellofresh salad" src="https://cdn.mos.cms.futurecdn.net/umKajccZk7gxbQAqH393Wk.jpg" mos="" align="middle" fullscreen="" width="3432" height="1930" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>Throughout my review, I found estimated prep times to be accurate, but you might need to budget more time if you're new to cooking or specific techniques. There was only one instance where I felt it was a bit off. For example, when roasting lamb for the Chermoula Honey Roast Lamb recipe, I was instructed to sear the lamb rump for 12 minutes in a pan before transferring it to the oven for an additional 25 minutes. </p><p>At the time, I had not roasted or seared lamb before, so I had little experience of what it should look like. It took me a while longer — around 16 minutes to full sear — pushing out the prep time to around 57 minutes, instead of the budgeted 40 minutes. Despite this minor hiccup, the meal turned out perfectly. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3006px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CwLF555Q3oveEmNivhwe8k" name="hellofresh-6" alt="HelloFresh meal preparation" src="https://cdn.mos.cms.futurecdn.net/CwLF555Q3oveEmNivhwe8k.jpg" mos="" align="middle" fullscreen="" width="3006" height="1691" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>Another meal we particularly enjoyed was the Crumbed Mexi-Spiced Chicken & Corn Tacos, which came with a small bottle of Cholula hot sauce. This meal was made easy by using an air fryer to cook the chicken, and came with a can of sweet corn for ease. If you don't own an air fryer, the recipe also provides details on how to pan-fry the chicken. </p><p>The recipe gave me great advice on how to crumb the chicken without making too much of a mess — by separating the crumbs, spices and egg into separate bowls — and how to chargrill the corn with a pan without burning it. This recipe budgeted 25 minutes from start to finish, and it was pretty accurate. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/4Cp74LdyduMHCbcQ9Dhj5m.jpg" alt="Hand holding Hellofresh meal" /><figcaption>An example of HelloFresh AU's ready meal selection<small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/L2kVTUPyatyXj4VQtvZrJn.jpg" alt="Hellofresh ready meal plated" /><figcaption>HelloFresh's ready meal plated<small role="credit">Future / Lucy Scotting</small></figcaption></figure></figure><p>Further, I did opt to include some ready meal pasta dishes to our weekly boxes and it was very interesting to say the least. As someone who had tried YouFoodz before, I was familiar with some of the recipes, and the options that HelloFresh had to offer were very much in line with the previous YouFoodz I've had. I did use these for a lazy dinner on a few evenings, and the instructions were as simple as expected — peel back the lid and pop it in the microwave for 3 minutes on high. Flavour-wise, the meals were pretty good and satisfied my hunger, but they definitely weren't Michelin-rated. </p><p>The only real gripe we had was flavour and portion sizes. We're self-proclaimed foodies, and love trying new dishes, so we were thrilled to have such a range of cuisines to choose from. However, we found we need to add more than just the provided spice mixes to enhance dishes. The flavour depth was slightly lacking, which might disappoint those who prefer bold, heavy seasoning. </p><p>The other niggle we had was portion sizes. As you can imagine, being foodies means we like to eat and some of our protein portions didn't quite sustain us. In some cases, we ended up making two meals and eating them family style to feel fulfilled. Now, HelloFresh suggests opting for a larger meal kit size if you're worried about this, but you can always upgrade or downgrade your plan at any time. There are some recipes that come with 'premium' protein ingredients for an additional fee, too, with larger meat/protein allocations, but we didn't feel the need to pay for these recipes. </p><h3 class="article-body__section" id="section-hellofresh-australia-review-verdict"><span>HelloFresh Australia review: Verdict</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3389px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="N3SiNJESmjapAnSDPEd9Lk" name="hellofresh-8" alt="HelloFresh meals on plates" src="https://cdn.mos.cms.futurecdn.net/N3SiNJESmjapAnSDPEd9Lk.jpg" mos="" align="middle" fullscreen="" width="3389" height="1906" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Lucy Scotting)</span></figcaption></figure><p>HelloFresh does exactly what you expect — takes the stress out of figuring out weekly dinners and offers the convenience of handpicked ingredients. I still had plenty of choice in what meals we got in every week, but it took immense pressure off hitting the shops after a long workday. Plus, with constant coupons offering savings for new subscribers, you could spend less than you would on takeaway every week. </p><p>Aside from that, the delivery was pretty seamless and I had no issue with editing meals or my order at any time in the lead-up to my next box. That said, I did spend some extra time prepping than allocated in some recipe cards, and portion sizes felt small, but overall, HelloFresh's meal kits didn't disappoint. And now with a more expanded range that includes air fryer-friendly meals and heat-and-eat dinners, there's much more accessibility on offer with HelloFresh's service. </p><p>So if you're looking for a simple and easy alternative to indecisive dinners, HelloFresh is a subscription box to consider.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I meal prep every Sunday — these are the 9 kitchen tools I can't live without ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I enjoy cooking — but not every single night. That's why Sunday meal prep has become a must for setting myself up for the week ahead. I'm not <em>always</em> this organized, but when I am, it makes busy weekdays so much easier (and a lot more enjoyable). And of course, I couldn't do it without a few of my favorite kitchen tools.</p><p>From the best food storage containers and my favorite Cuisinart knives to functional vegetable dicers and the Ninja Flip air fryer that I love, I've rounded up my favorite kitchen tools and appliances that make Sunday meal prep a total breeze. Keep scrolling to check them out. </p>        <div class="featured_product_block featured_block_hero" data-id="4acfe432-8b5f-11f1-a290-a780ba8db859">            <a href="https://www.amazon.com/Pyrex-BPA-Free-Containers-Dishwasher-Microwave/dp/B0000BYCGF/" data-model-name="Simply Store (3-Pack) Glass Food Storage Containers Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/cm3mdXRQ5SKWa4Ag33NixN.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Pyrex</div>                                        <div class="featured__title">Simply Store (3-Pack) Glass Food Storage Containers Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe4be-8b5f-11f1-81d0-f5f5344dfc4f">            <a href="https://www.amazon.com/INSETLAN-Glass-Overnight-Containers-6-Pack/dp/B0GLF86YR5/" data-model-name="Glass Overnight Oats Containers With Lids" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ecVzzjzLwLtYJyzuRzmwzN.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>INSETLAN</div>                                        <div class="featured__title">Glass Overnight Oats Containers With Lids</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe540-8b5f-11f1-a976-bd94d2366f3c">            <a href="https://www.amazon.com/Cuisinart-C55-01-12PCKS-Advantage-Collection-Multicolor/dp/B00FLQ4EE6/" data-model-name="12-Piece Knife Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/mu62AoZFJeQSuhZ33HpH4P.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Cuisinart</div>                                        <div class="featured__title">12-Piece Knife Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe5b8-8b5f-11f1-97a4-3f8c8c261b17">            <a href="https://www.amazon.com/Fullstar-Vegetable-Chopper-Spiralizer-Container/dp/B0C1T6J3CS" data-model-name="Pro Original Vegetable Chopper Kitchen Prep Tool" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/is4KqTcSfgA8jVkVWk9JCP.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>fullstar</div>                                        <div class="featured__title">Pro Original Vegetable Chopper Kitchen Prep Tool</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe630-8b5f-11f1-bc66-0f065d26b756">            <a href="https://www.amazon.com/COOK-COLOR-Mixing-Bowls-Lids/dp/B0CKTV5ZQK/" data-model-name="Mixing Bowls With Lids - 12 Piece Plastic Nesting Bowls Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/8Z8vLh3W26HXN6ZZNHUi6P.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>COOK WITH COLOR</div>                                        <div class="featured__title">Mixing Bowls With Lids - 12 Piece Plastic Nesting Bowls Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe69e-8b5f-11f1-801d-5b46ee4754e4">            <a href="https://www.amazon.com/Rubbermaid-Brilliance-Containers-2-Compartment-Storage/dp/B08WR1P45C/" data-model-name="Brilliance Meal Prep Containers" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/wDtRr9BcJPFMwGrUfYoiJP.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Rubbermaid</div>                                        <div class="featured__title">Brilliance Meal Prep Containers</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe70c-8b5f-11f1-9eb6-4f2824f70b11">            <a href="https://www.amazon.com/bamboo-cutting-board-plastic-board/dp/B08DHTN88H/" data-model-name="Bamboo Cutting Board Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/eLDiKewxKT2WLqHr5KFDFP.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Cooler Kitchen</div>                                        <div class="featured__title">Bamboo Cutting Board Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe784-8b5f-11f1-b0e8-f3ef8d903f71">            <a href="https://www.amazon.com/Cuisinart-Small-Processor-Auto-Reversing-Smart/dp/B074MQFR55/" data-model-name="Small Food Processor" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/cM6yPs47QAESHfsbZPjPCP.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Cuisinart</div>                                        <div class="featured__title">Small Food Processor</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe7f2-8b5f-11f1-a8a5-6d95df047824">            <a href="https://www.amazon.com/Ninja-Functionality-Capability-Removable-SP151/dp/B0D1CXL52G" data-model-name="Flip Toaster Oven & Air Fryer, 8-In-1" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/yiS54tocCJm3byMNGuReJP.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ninja</div>                                        <div class="featured__title">Flip Toaster Oven & Air Fryer, 8-In-1</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/i-meal-prep-every-sunday-these-are-the-9-kitchen-tools-i-cant-live-without</link>
                                                                            <description>
                            <![CDATA[ I'm rounding up the meal prep tools I use every Sunday. Here are my favorites from Cuisinart, Ninja and more. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">cyNGFXgEoSppfXViydjuKF</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/SAaXzA992K8hBi9oVKxSrk-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 29 Jul 2026 15:52:22 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                                    <dc:creator><![CDATA[ Olivia Halevy ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/YAg7Fmh9dGKbg3hULBMWiU.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Olivia is a Deals Writer at Tom’s Guide. She loves all things lifestyle, shopping and sourcing the best deals from some of the top brands, retailers and websites around. From fashion and beauty to smart home and tech, she has an eye for finding practical, stylish, and reasonably-priced items that help make life simpler (and a little more aesthetically-pleasing).&amp;nbsp;Olivia has over five years of experience writing lifestyle, commerce, and branded content for &lt;a href=&quot;http://patch.com/&quot;&gt;Patch.com&lt;/a&gt;. She&#039;s covered major shopping holidays like Amazon Prime Day, Black Friday and Cyber Monday. In her free time, she enjoys trying out new exercise classes, binge-watching the latest viral TV shows, cooking, spending time with family and taking long walks with her dog, Gus.&amp;nbsp;&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/SAaXzA992K8hBi9oVKxSrk-1280-80.jpg">
                                                            <media:credit><![CDATA[Amazon]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Amazon deals]]></media:description>                                                            <media:text><![CDATA[Amazon deals]]></media:text>
                                <media:title type="plain"><![CDATA[Amazon deals]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/SAaXzA992K8hBi9oVKxSrk-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I enjoy cooking — but not every single night. That's why Sunday meal prep has become a must for setting myself up for the week ahead. I'm not <em>always</em> this organized, but when I am, it makes busy weekdays so much easier (and a lot more enjoyable). And of course, I couldn't do it without a few of my favorite kitchen tools.</p><p>From the best food storage containers and my favorite Cuisinart knives to functional vegetable dicers and the Ninja Flip air fryer that I love, I've rounded up my favorite kitchen tools and appliances that make Sunday meal prep a total breeze. Keep scrolling to check them out. </p>        <div class="featured_product_block featured_block_hero" data-id="4acfe432-8b5f-11f1-a290-a780ba8db859">            <a href="https://www.amazon.com/Pyrex-BPA-Free-Containers-Dishwasher-Microwave/dp/B0000BYCGF/" data-model-name="Simply Store (3-Pack) Glass Food Storage Containers Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/cm3mdXRQ5SKWa4Ag33NixN.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Pyrex</div>                                        <div class="featured__title">Simply Store (3-Pack) Glass Food Storage Containers Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe4be-8b5f-11f1-81d0-f5f5344dfc4f">            <a href="https://www.amazon.com/INSETLAN-Glass-Overnight-Containers-6-Pack/dp/B0GLF86YR5/" data-model-name="Glass Overnight Oats Containers With Lids" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ecVzzjzLwLtYJyzuRzmwzN.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>INSETLAN</div>                                        <div class="featured__title">Glass Overnight Oats Containers With Lids</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe540-8b5f-11f1-a976-bd94d2366f3c">            <a href="https://www.amazon.com/Cuisinart-C55-01-12PCKS-Advantage-Collection-Multicolor/dp/B00FLQ4EE6/" data-model-name="12-Piece Knife Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/mu62AoZFJeQSuhZ33HpH4P.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Cuisinart</div>                                        <div class="featured__title">12-Piece Knife Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe5b8-8b5f-11f1-97a4-3f8c8c261b17">            <a href="https://www.amazon.com/Fullstar-Vegetable-Chopper-Spiralizer-Container/dp/B0C1T6J3CS" data-model-name="Pro Original Vegetable Chopper Kitchen Prep Tool" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/is4KqTcSfgA8jVkVWk9JCP.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>fullstar</div>                                        <div class="featured__title">Pro Original Vegetable Chopper Kitchen Prep Tool</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe630-8b5f-11f1-bc66-0f065d26b756">            <a href="https://www.amazon.com/COOK-COLOR-Mixing-Bowls-Lids/dp/B0CKTV5ZQK/" data-model-name="Mixing Bowls With Lids - 12 Piece Plastic Nesting Bowls Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/8Z8vLh3W26HXN6ZZNHUi6P.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>COOK WITH COLOR</div>                                        <div class="featured__title">Mixing Bowls With Lids - 12 Piece Plastic Nesting Bowls Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe69e-8b5f-11f1-801d-5b46ee4754e4">            <a href="https://www.amazon.com/Rubbermaid-Brilliance-Containers-2-Compartment-Storage/dp/B08WR1P45C/" data-model-name="Brilliance Meal Prep Containers" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/wDtRr9BcJPFMwGrUfYoiJP.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Rubbermaid</div>                                        <div class="featured__title">Brilliance Meal Prep Containers</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe70c-8b5f-11f1-9eb6-4f2824f70b11">            <a href="https://www.amazon.com/bamboo-cutting-board-plastic-board/dp/B08DHTN88H/" data-model-name="Bamboo Cutting Board Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/eLDiKewxKT2WLqHr5KFDFP.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Cooler Kitchen</div>                                        <div class="featured__title">Bamboo Cutting Board Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe784-8b5f-11f1-b0e8-f3ef8d903f71">            <a href="https://www.amazon.com/Cuisinart-Small-Processor-Auto-Reversing-Smart/dp/B074MQFR55/" data-model-name="Small Food Processor" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/cM6yPs47QAESHfsbZPjPCP.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Cuisinart</div>                                        <div class="featured__title">Small Food Processor</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4acfe7f2-8b5f-11f1-a8a5-6d95df047824">            <a href="https://www.amazon.com/Ninja-Functionality-Capability-Removable-SP151/dp/B0D1CXL52G" data-model-name="Flip Toaster Oven & Air Fryer, 8-In-1" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.37%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/yiS54tocCJm3byMNGuReJP.jpg" alt="Meal Prep Deals"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ninja</div>                                        <div class="featured__title">Flip Toaster Oven & Air Fryer, 8-In-1</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I spent a month eating virtually nothing but pizza to test the Gozney Arc XL — here’s the outcome ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Gozney Arc XL is all the pizza oven most people will ever need. As you might’ve guessed from the name, it’s the larger variant of the <a href="https://www.tomsguide.com/home/kitchen-dining/gozney-arc-pizza-oven-review">Gozney Arc</a>, which we’ve also reviewed and loved.</p><p>The Arc XL sits below Gozney’s Dome / <a href="https://www.tomsguide.com/home/kitchen-dining/gozney-dome-xl-2-review">Dome XL (Gen 2)</a>, which are enormous and can fit multiple pizzas on their dual stones. The Arc XL is intended for those without the enormous amounts of space and cash it takes to get to a Dome. Nevertheless, the Arc XL should be plenty for most home pizzaioli, and even small-time commercial ventures.</p><p>Running on gas, it offers high heat and stable temperatures, plus it’s easy to use thanks to the digital display and simple controls. It’s beautifully styled and made, and will be a complement to any patio or pizza shelter. </p><p>It isn’t perfect. The elephant in the room is the Arc XL’s high price. Gozney also makes its stones quite tricky to remove versus Ooni, which is a problem if you get the front of the stone (which doesn’t get very hot) messy.</p><p>But could this be the <a href="https://www.tomsguide.com/best-picks/best-outdoor-pizza-ovens">best outdoor pizza oven</a> for you? Probably! Read my full Gozney Arc XL review to find out more.</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-specs"><span>Gozney Arc XL review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Gozney-Pizza-Fired-makes-pizza/dp/B0CY76J1NY/" target="_blank" rel="nofollow">$999</a> / <a href="https://www.gozney.com/products/arc-xl" target="_blank" rel="nofollow">£799</a></p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>20.9 x 24.8 x 13.5 inches (530 x 629x 342mm)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>58.5lbs (26.5kg) </p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>1x 16-inch pizza</p></td></tr><tr><td class="firstcol " ><p><strong>Internal dimensions</strong></p></td><td  ><p>16.8 x 20.3 x 6.8 inches (427 x 517 x 173mm)</p></td></tr><tr><td class="firstcol " ><p><strong>Mouth opening</strong></p></td><td  ><p>16.8 x 3.6 inches (427 x 95mm)</p><p><br></p></td></tr><tr><td class="firstcol " ><p><strong>Stone</strong></p></td><td  ><p>0.8-inch/20mm cordierite</p></td></tr><tr><td class="firstcol " ><p><strong>Max temperature</strong></p></td><td  ><p>932F / 500C</p></td></tr><tr><td class="firstcol " ><p><strong>Fuel types</strong></p></td><td  ><p>Propane</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-gozney-arc-xl-review-price-availability"><span>Gozney Arc XL review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iRGEKpzFxPiWaA7PHzNNZg" name="Gozney Arc XL-11" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/iRGEKpzFxPiWaA7PHzNNZg.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Gozney Arc XL isn’t cheap, costing <a href="https://www.amazon.com/Gozney-Pizza-Fired-makes-pizza/dp/B0CY76J1NY/" target="_blank" rel="nofollow">$999</a> / <a href="https://www.gozney.com/products/arc-xl" target="_blank" rel="nofollow">£799</a>, which is a sizable $300 / £200 more than the 14-inch standard Arc. Other than the extra 2-inch capacity, the ovens are pretty much the same, so it’ll just depend on how much larger pizzas mean to you.</p><p>Personally, I love cooking larger pies. It’s trickier, so more of a challenge, but when you get it right you can feed more people easily, so the XL will be better if you plan on cooking for multiple guests regularly.</p><p>The Arc XL’s main rival is the <a href="https://www.tomsguide.com/reviews/ooni-koda-16">Ooni Koda 16</a>, which cooks 16-inch pizzas, runs on gas and undercuts the Gozney by a healthy margin, costing $599 / £499. The Koda is lighter, uses Ooni’s folding leg system for easier storage, and features an L-shaped burner for wider flame dispersion and less rotating required during cooking. We awarded the Koda 16 our full five stars, and you can probably now see why — for the money, it’s difficult to beat.</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-design"><span>Gozney Arc XL review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nUmSESdp7rcda2nZUq8cHj" name="Gozney Arc XL-4" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/nUmSESdp7rcda2nZUq8cHj.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Gozney Arc XL looks beautiful, employing Gozney’s typically slick, neo-retro aesthetic. It looks straight out of a trendy London pizzeria, which is the point. It’s beautifully made and finished, with a high level of attention to detail, and feels built to last. </p><p>I wouldn’t say it’s any better or worse than an Ooni in this respect, so which manufacturer you pick will depend on whether you want the more luxurious styling of Gozney, or the more futuristic look of Ooni. That is unless you’re a commercial user — if you’ve got a pizza van or small kitchen to kit out, I think the round shape and flat base of the Gozney is more versatile than the Ooni Karu 16, with its oblong shape and tall legs.</p><p>The Arc XL is a behemoth, though, measuring 20.9 x 24.8 x 13.5 inches and weighing nearly 60 pounds, so you’ll want a second person on hand to help you set up. My wife was on vacation so I had no choice but to set the Arc XL up on my own, and very nearly put my back out getting it unboxed and onto the stand. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="hf9vo6tiRYH8CFqHhXbo6h" name="Gozney Arc XL-10" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/hf9vo6tiRYH8CFqHhXbo6h.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Colossal weight aside, setting the Arc XL up is very easy — you just remove the packaging and padding; pull the stone forward a fraction of an inch; put the metal flame guard in place; and put batteries (supplied) in the thermometer and digital dial. There’s a helpful quick start guide included to cover all this. </p><p>I was ready for the burn-in after about 10 minutes — burning in is something you must do with most pizza ovens before you can make pizza — although it actually took me around 5 days because I couldn't find any fuel in my area (it was peak summer).</p><h2 id="fuel">Fuel</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="3E7XXTr3qkrekwYi33Pb7h" name="Gozney Arc XL-9" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/3E7XXTr3qkrekwYi33Pb7h.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Arc XL runs on propane gas. You can theoretically use butane, too, although Gozney doesn’t recommend it, and butane is less likely to work in colder temperatures. My Arc XL was supplied with a clip-on regulator for the patio gas canisters we get here in the U.K.</p><p>Something I love about multi-fuel ovens is that, if you’re out of gas, you can simply use wood or charcoal. It’s worth considering a multi-fuel oven like the Gozney Dome or Ooni Karu 2 Pro if you’d rather have that extra flexibility.</p><p>The Arc XL is fairly fuel-efficient and Gozney says a 28lb / 13kg canister should last around 30 hours. You’ll need as much efficiency as you can get, as the only way to easily clean the Arc XL is by burning it for three hours, which sucks, as I’ll touch on later.</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-accessories"><span>Gozney Arc XL review: Accessories</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="tA4CNWKTdRCrAk4xaz6TTj" name="Gozney Arc XL-14" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/tA4CNWKTdRCrAk4xaz6TTj.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Something many don’t understand before they buy their first pizza oven is that you need accessories. You can’t make a pizza without at least a launching peel and a turning peel. I tested <a href="https://www.amazon.com/Gozney-Pro-Pizza-Placement-Peel/dp/B0FS2Y61RQ" target="_blank" rel="nofollow">Gozney’s 16-inch Pro Placement peel</a> ($109) and <a href="https://us.gozney.com/products/balance-turning-peel" target="_blank" rel="nofollow">Balance Turning peel</a> ($89) and would highly recommend them both. Although, if you’re just starting out or are on a tight budget, you can find cheaper alternatives at Amazon, which will work just fine.</p><p>I would also recommend you buy an infrared thermometer for measuring stone temperature (the temperature on the Arc XL only measures ambient temperature, which won’t tell you precisely when the stone is ready for cooking). The <a href="https://us.gozney.com/products/infrared-thermometer" target="_blank" rel="nofollow">Gozney infrared thermometer gun costs $49</a>, which is similar to Ooni’s pricing.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="PzLoS8iGREiCXx2jpUAGsh" name="Gozney Arc XL-15" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/PzLoS8iGREiCXx2jpUAGsh.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Gozney also sells a variety of other accessories that I found exceptionally useful. Firstly, there’s the <a href="https://us.gozney.com/products/dough-tray" target="_blank" rel="nofollow">$49 dough tray</a>, which I absolutely love. This tray features a thermometer to show you which type of proof (cold, warm, etc) your ambient conditions will result in, allowing you to tailor proof times. It also shows you when conditions are too warm, and features a bulk fermenting tray and a silicone insert for six dough balls (for up to 14-inch pizzas).</p><p>I’m also a huge fan of the <a href="https://us.gozney.com/products/arc-xl-mantel" target="_blank" rel="nofollow">Arc XL mantel ($119)</a>, which gives you a couple of inches of surface outside the oven mouth — enough space to safely turn food or leave your pizza to rest for a moment without burning while you find a board for it. And <a href="https://us.gozney.com/products/heat-resistant-gloves" target="_blank" rel="nofollow">Gozney's $39 heat resistant gloves</a>, which can stand up to 950F / 500C heat.</p><p>I also tested the <a href="https://us.gozney.com/products/pizza-cutter" target="_blank" rel="nofollow">Gozney Pizza Cutter ($44)</a>. My wife hated pizza cutters and always refused to use our other ones (choosing a knife or scissors instead). The Gozney cutter has converted her — thanks to its weight and sharp blade, it's much more stable and effective than cheaper cutters.</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-performance"><span>Gozney Arc XL review: Performance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5217px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="cT8UcMDUv9ZCLk6XCrsssn" name="Gozney Arc XL_Test shot-6" alt="A pizza cooking in the Gozney Arc XL" src="https://cdn.mos.cms.futurecdn.net/cT8UcMDUv9ZCLk6XCrsssn.jpg" mos="" align="middle" fullscreen="" width="5217" height="2935" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Gozney Arc XL performs like a professional pizza oven. The stone heats up to 750F / 400C in around 15-20 minutes, which is hot enough for Neapolitan-style pizza. Alternatively, you can cook other styles of pizza before then — low’n’slow pizzas like NY-style.</p><p>The thermometer gauge on the front of the oven is very useful to give you an idea of ambient temperatures, which dictate how quickly the upper sections of your pizza will cook (although the cooking of your toppings will also be affected by the size and intensity of the gas flame). </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="qo2R3dgpxboVzaiW4Xb8pg" name="Gozney Arc XL-13" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/qo2R3dgpxboVzaiW4Xb8pg.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Annoyingly, the temperature gauge turns itself off after a while — on a few occasions I turned back to the gauge while cooking to check temperatures, only to find it switched off. I get that this is to save battery, but it’s really annoying.</p><p>The Arc XL is very predictable thanks to running on gas, and you have a high level of control over the flame size by twisting the control knob on the front.</p><p>You’ll need to cook a few pizzas and get used to the temperature of the oven, and to get a feel for how to adjust your flame while cooking. Once you’ve got it down, though, you can cook a Neapolitan pizza in just a minute or two.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/oWB4cCGpNUQVvcxoftMxhm.jpg" alt="A slice of pizza in hand, the pizza was cooked on the Gozney Arc XL" /><figcaption><small role="credit">Peter Wolinski / Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EBQYdvgx7xoLoEySJVdxsn.jpg" alt="A pizza cooked on the Gozney Arc XL" /><figcaption><small role="credit">Peter Wolinski / Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/yMwi7zeD3wrkj7PrzjXqun.jpg" alt="A pizza cooked on the Gozney Arc XL" /><figcaption><small role="credit">Peter Wolinski / Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ae59Fbbpv3Jk3hP3w9pNun.jpg" alt="A pizza cooked on the Gozney Arc XL" /><figcaption><small role="credit">Peter Wolinski / Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LGHQdEdiGemfaSrK9D2jun.jpg" alt="A pizza cooked on the Gozney Arc XL" /><figcaption><small role="credit">Peter Wolinski / Future</small></figcaption></figure></figure><p>I cooked a variety of pizzas, from 12-inch through to 16-inch bases, in Neapolitan and NY styles. On several occasions, I cooked for groups of six. The Arc XL handled it all beautifully. Thanks to the large 16-inch stone, you can cook bigger pizzas to feed multiple people, so you don’t need to spend the entirety of a pizza party churning out 12-inch pies for each person.</p><p>It’s very easy to burn larger 16-inch pies, as they sit so close to the flame. This means that you’ll want to turn them early, which can cause rips in the dough and an awful mess. This happened on a few occasions for me — to avoid this, ensure the stone is at least 750F / 400C before launching, so pizza bases cook very quickly.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4770px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LkKuqvfb2h4c9TcEEg9Ctn" name="Gozney Arc XL_Test shot-4" alt="A garlic flatbread cooked on the Gozney Arc XL" src="https://cdn.mos.cms.futurecdn.net/LkKuqvfb2h4c9TcEEg9Ctn.jpg" mos="" align="middle" fullscreen="" width="4770" height="2683" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>I also cooked large garlic flatbreads, as well as sides like charred broccoli spears and caramelized roasted carrots. The stone also gets hot enough to sear steaks and meat, so it’s a highly versatile oven.</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-storage-maintenance"><span>Gozney Arc XL review: Storage & maintenance</span></h2><p>My one major gripe with the Arc XL is how difficult the stone is to remove. Without buying a removal kit, you’ll need to tip the oven on its side and support the stone out — a two-person job, which also involves removing the oven from any stand or base you have it fastened to. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="cQ5L2r3DpjfDFQPvDFJF6h" name="Gozney Arc XL-6" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/cQ5L2r3DpjfDFQPvDFJF6h.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>This is infuriating if you make a mess. Gonzey’s advice is to burn the oven for 3 hours to clean it (wasting gas), which works, but only on the middle and rear of the stone. The front never gets hot enough to self-clean, so if you make a mess there when launching or removing a failed launch, you can’t clean it properly without a lot of hassle. You can scrape most mess off when cool, but not everything. And besides, food still gets stuck between the stone and the oven lip.</p><p>I love that Ooni’s stones are much easier to remove, so you can take them out and properly clean them up. As I mentioned before, this also means you don’t need to waste hours of gas just to clean. Gozney should include a stone removal kit with every oven.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="JovgEojWbc9cun2NkppDth" name="Gozney Arc XL-3" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/JovgEojWbc9cun2NkppDth.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Gozney also recommends burning the oven for several hours to clean soot from the roof, which is just ridiculously wasteful. I just waited for it to cool and cleaned with a dry cloth. Maintenance is otherwise very simple — just wipe down the exterior parts when cool.</p><p>Storage is as you’d expect for such a huge cooker: you’ll need a fair bit of space. I’d recommend <a href="https://us.gozney.com/products/arc-arc-xl-stand" target="_blank" rel="nofollow">Gozney’s Arc + Arc XL stand</a> ($299), which features fold-away ledges, storage hooks, and wheels to move the oven to and from position. It’s also important to cover the oven and keep it safe from the elements — Gozney sells covers for this (including a <a href="https://us.gozney.com/products/arc-arc-xl-stand-cover" target="_blank" rel="nofollow">cover that goes over the whole stand</a>).</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-verdict"><span>Gozney Arc XL review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="diNPFJT9L76PJjBZeYjrJh" name="Gozney Arc XL-2" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/diNPFJT9L76PJjBZeYjrJh.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Gozney Arc XL is a fantastic pizza oven. With its large 16-inch stone, you can cook big pizzas for multiple people and simplify your pizza parties. It’s easy to use, beautiful to behold and built to last. And most importantly, it makes fantastic pizza.</p><p>There are a couple of things to consider. First, it’s pricey — you can get the rival Ooni Karu 16 for much less money, which is better than the Arc XL in almost every way. Secondly, the stone is tricky to remove, which makes the Arc XL trickier to clean than Ooni ovens. </p><p>If you’re willing to overlook those flaws, though, as I have, then I’ve no hesitation in recommending the Arc XL to small commercial and home pizzaioli. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/gozney-arc-xl-review</link>
                                                                            <description>
                            <![CDATA[ The Gozney Arc XL makes great pizza up to 16 inches, and is super easy to use. It’s a little pricey and tricky to clean though. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">feRsDk9CT9VPxhDGkm66Ld</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/oYsS42iYBUREEo4md89qj3-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 29 Jul 2026 15:10:02 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ peter.wolinski@futurenet.com (Peter Wolinski) ]]></author>                    <dc:creator><![CDATA[ Peter Wolinski ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/stgPfXWY7ukw8J8rfC7vjg.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Peter is a Senior Editor at Tom&#039;s Guide, heading up the site&#039;s Reviews team and Cameras section. Having built gaming PCs since he was 10 (that&#039;s a while ago now) he&#039;s a bit of a nerd about components and hardware. He&#039;s also been an iPhone user since the classic iPhone 4, and a Mac user for well over a decade. Experienced in using and testing all kinds of technology — from phones through to tablets, computers, games consoles, cameras and smart home tech — helping people find the best tech for them (at the best prices) is what Peter does best. A photographer since he bought his first camera (a Fujifilm) in 2015, Peter was previously an Editor for Canon-Europe.com. He then edited the Cameras and How To sections of Tom&#039;s Guide. When he&#039;s not crafting helpful, in-depth reviews, Peter can usually be found out and about honing his architectural photography skills, riding his motorcycle around Welsh mountain roads, telling everyone about his two greyhounds, squeezing a few extra FPS out of PC games or perfecting his espresso shots.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/oYsS42iYBUREEo4md89qj3-1280-80.jpg">
                                                            <media:credit><![CDATA[Peter Wolinski / Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A split image showing a slice of pizza on the left and the Gozney Arc XL on the right]]></media:description>                                                            <media:text><![CDATA[A split image showing a slice of pizza on the left and the Gozney Arc XL on the right]]></media:text>
                                <media:title type="plain"><![CDATA[A split image showing a slice of pizza on the left and the Gozney Arc XL on the right]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/oYsS42iYBUREEo4md89qj3-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Gozney Arc XL is all the pizza oven most people will ever need. As you might’ve guessed from the name, it’s the larger variant of the <a href="https://www.tomsguide.com/home/kitchen-dining/gozney-arc-pizza-oven-review">Gozney Arc</a>, which we’ve also reviewed and loved.</p><p>The Arc XL sits below Gozney’s Dome / <a href="https://www.tomsguide.com/home/kitchen-dining/gozney-dome-xl-2-review">Dome XL (Gen 2)</a>, which are enormous and can fit multiple pizzas on their dual stones. The Arc XL is intended for those without the enormous amounts of space and cash it takes to get to a Dome. Nevertheless, the Arc XL should be plenty for most home pizzaioli, and even small-time commercial ventures.</p><p>Running on gas, it offers high heat and stable temperatures, plus it’s easy to use thanks to the digital display and simple controls. It’s beautifully styled and made, and will be a complement to any patio or pizza shelter. </p><p>It isn’t perfect. The elephant in the room is the Arc XL’s high price. Gozney also makes its stones quite tricky to remove versus Ooni, which is a problem if you get the front of the stone (which doesn’t get very hot) messy.</p><p>But could this be the <a href="https://www.tomsguide.com/best-picks/best-outdoor-pizza-ovens">best outdoor pizza oven</a> for you? Probably! Read my full Gozney Arc XL review to find out more.</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-specs"><span>Gozney Arc XL review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Gozney-Pizza-Fired-makes-pizza/dp/B0CY76J1NY/" target="_blank" rel="nofollow">$999</a> / <a href="https://www.gozney.com/products/arc-xl" target="_blank" rel="nofollow">£799</a></p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>20.9 x 24.8 x 13.5 inches (530 x 629x 342mm)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>58.5lbs (26.5kg) </p></td></tr><tr><td class="firstcol " ><p><strong>Capacity</strong></p></td><td  ><p>1x 16-inch pizza</p></td></tr><tr><td class="firstcol " ><p><strong>Internal dimensions</strong></p></td><td  ><p>16.8 x 20.3 x 6.8 inches (427 x 517 x 173mm)</p></td></tr><tr><td class="firstcol " ><p><strong>Mouth opening</strong></p></td><td  ><p>16.8 x 3.6 inches (427 x 95mm)</p><p><br></p></td></tr><tr><td class="firstcol " ><p><strong>Stone</strong></p></td><td  ><p>0.8-inch/20mm cordierite</p></td></tr><tr><td class="firstcol " ><p><strong>Max temperature</strong></p></td><td  ><p>932F / 500C</p></td></tr><tr><td class="firstcol " ><p><strong>Fuel types</strong></p></td><td  ><p>Propane</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-gozney-arc-xl-review-price-availability"><span>Gozney Arc XL review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iRGEKpzFxPiWaA7PHzNNZg" name="Gozney Arc XL-11" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/iRGEKpzFxPiWaA7PHzNNZg.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Gozney Arc XL isn’t cheap, costing <a href="https://www.amazon.com/Gozney-Pizza-Fired-makes-pizza/dp/B0CY76J1NY/" target="_blank" rel="nofollow">$999</a> / <a href="https://www.gozney.com/products/arc-xl" target="_blank" rel="nofollow">£799</a>, which is a sizable $300 / £200 more than the 14-inch standard Arc. Other than the extra 2-inch capacity, the ovens are pretty much the same, so it’ll just depend on how much larger pizzas mean to you.</p><p>Personally, I love cooking larger pies. It’s trickier, so more of a challenge, but when you get it right you can feed more people easily, so the XL will be better if you plan on cooking for multiple guests regularly.</p><p>The Arc XL’s main rival is the <a href="https://www.tomsguide.com/reviews/ooni-koda-16">Ooni Koda 16</a>, which cooks 16-inch pizzas, runs on gas and undercuts the Gozney by a healthy margin, costing $599 / £499. The Koda is lighter, uses Ooni’s folding leg system for easier storage, and features an L-shaped burner for wider flame dispersion and less rotating required during cooking. We awarded the Koda 16 our full five stars, and you can probably now see why — for the money, it’s difficult to beat.</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-design"><span>Gozney Arc XL review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nUmSESdp7rcda2nZUq8cHj" name="Gozney Arc XL-4" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/nUmSESdp7rcda2nZUq8cHj.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Gozney Arc XL looks beautiful, employing Gozney’s typically slick, neo-retro aesthetic. It looks straight out of a trendy London pizzeria, which is the point. It’s beautifully made and finished, with a high level of attention to detail, and feels built to last. </p><p>I wouldn’t say it’s any better or worse than an Ooni in this respect, so which manufacturer you pick will depend on whether you want the more luxurious styling of Gozney, or the more futuristic look of Ooni. That is unless you’re a commercial user — if you’ve got a pizza van or small kitchen to kit out, I think the round shape and flat base of the Gozney is more versatile than the Ooni Karu 16, with its oblong shape and tall legs.</p><p>The Arc XL is a behemoth, though, measuring 20.9 x 24.8 x 13.5 inches and weighing nearly 60 pounds, so you’ll want a second person on hand to help you set up. My wife was on vacation so I had no choice but to set the Arc XL up on my own, and very nearly put my back out getting it unboxed and onto the stand. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="hf9vo6tiRYH8CFqHhXbo6h" name="Gozney Arc XL-10" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/hf9vo6tiRYH8CFqHhXbo6h.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Colossal weight aside, setting the Arc XL up is very easy — you just remove the packaging and padding; pull the stone forward a fraction of an inch; put the metal flame guard in place; and put batteries (supplied) in the thermometer and digital dial. There’s a helpful quick start guide included to cover all this. </p><p>I was ready for the burn-in after about 10 minutes — burning in is something you must do with most pizza ovens before you can make pizza — although it actually took me around 5 days because I couldn't find any fuel in my area (it was peak summer).</p><h2 id="fuel">Fuel</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="3E7XXTr3qkrekwYi33Pb7h" name="Gozney Arc XL-9" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/3E7XXTr3qkrekwYi33Pb7h.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Arc XL runs on propane gas. You can theoretically use butane, too, although Gozney doesn’t recommend it, and butane is less likely to work in colder temperatures. My Arc XL was supplied with a clip-on regulator for the patio gas canisters we get here in the U.K.</p><p>Something I love about multi-fuel ovens is that, if you’re out of gas, you can simply use wood or charcoal. It’s worth considering a multi-fuel oven like the Gozney Dome or Ooni Karu 2 Pro if you’d rather have that extra flexibility.</p><p>The Arc XL is fairly fuel-efficient and Gozney says a 28lb / 13kg canister should last around 30 hours. You’ll need as much efficiency as you can get, as the only way to easily clean the Arc XL is by burning it for three hours, which sucks, as I’ll touch on later.</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-accessories"><span>Gozney Arc XL review: Accessories</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="tA4CNWKTdRCrAk4xaz6TTj" name="Gozney Arc XL-14" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/tA4CNWKTdRCrAk4xaz6TTj.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Something many don’t understand before they buy their first pizza oven is that you need accessories. You can’t make a pizza without at least a launching peel and a turning peel. I tested <a href="https://www.amazon.com/Gozney-Pro-Pizza-Placement-Peel/dp/B0FS2Y61RQ" target="_blank" rel="nofollow">Gozney’s 16-inch Pro Placement peel</a> ($109) and <a href="https://us.gozney.com/products/balance-turning-peel" target="_blank" rel="nofollow">Balance Turning peel</a> ($89) and would highly recommend them both. Although, if you’re just starting out or are on a tight budget, you can find cheaper alternatives at Amazon, which will work just fine.</p><p>I would also recommend you buy an infrared thermometer for measuring stone temperature (the temperature on the Arc XL only measures ambient temperature, which won’t tell you precisely when the stone is ready for cooking). The <a href="https://us.gozney.com/products/infrared-thermometer" target="_blank" rel="nofollow">Gozney infrared thermometer gun costs $49</a>, which is similar to Ooni’s pricing.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="PzLoS8iGREiCXx2jpUAGsh" name="Gozney Arc XL-15" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/PzLoS8iGREiCXx2jpUAGsh.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Gozney also sells a variety of other accessories that I found exceptionally useful. Firstly, there’s the <a href="https://us.gozney.com/products/dough-tray" target="_blank" rel="nofollow">$49 dough tray</a>, which I absolutely love. This tray features a thermometer to show you which type of proof (cold, warm, etc) your ambient conditions will result in, allowing you to tailor proof times. It also shows you when conditions are too warm, and features a bulk fermenting tray and a silicone insert for six dough balls (for up to 14-inch pizzas).</p><p>I’m also a huge fan of the <a href="https://us.gozney.com/products/arc-xl-mantel" target="_blank" rel="nofollow">Arc XL mantel ($119)</a>, which gives you a couple of inches of surface outside the oven mouth — enough space to safely turn food or leave your pizza to rest for a moment without burning while you find a board for it. And <a href="https://us.gozney.com/products/heat-resistant-gloves" target="_blank" rel="nofollow">Gozney's $39 heat resistant gloves</a>, which can stand up to 950F / 500C heat.</p><p>I also tested the <a href="https://us.gozney.com/products/pizza-cutter" target="_blank" rel="nofollow">Gozney Pizza Cutter ($44)</a>. My wife hated pizza cutters and always refused to use our other ones (choosing a knife or scissors instead). The Gozney cutter has converted her — thanks to its weight and sharp blade, it's much more stable and effective than cheaper cutters.</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-performance"><span>Gozney Arc XL review: Performance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5217px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="cT8UcMDUv9ZCLk6XCrsssn" name="Gozney Arc XL_Test shot-6" alt="A pizza cooking in the Gozney Arc XL" src="https://cdn.mos.cms.futurecdn.net/cT8UcMDUv9ZCLk6XCrsssn.jpg" mos="" align="middle" fullscreen="" width="5217" height="2935" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Gozney Arc XL performs like a professional pizza oven. The stone heats up to 750F / 400C in around 15-20 minutes, which is hot enough for Neapolitan-style pizza. Alternatively, you can cook other styles of pizza before then — low’n’slow pizzas like NY-style.</p><p>The thermometer gauge on the front of the oven is very useful to give you an idea of ambient temperatures, which dictate how quickly the upper sections of your pizza will cook (although the cooking of your toppings will also be affected by the size and intensity of the gas flame). </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="qo2R3dgpxboVzaiW4Xb8pg" name="Gozney Arc XL-13" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/qo2R3dgpxboVzaiW4Xb8pg.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Annoyingly, the temperature gauge turns itself off after a while — on a few occasions I turned back to the gauge while cooking to check temperatures, only to find it switched off. I get that this is to save battery, but it’s really annoying.</p><p>The Arc XL is very predictable thanks to running on gas, and you have a high level of control over the flame size by twisting the control knob on the front.</p><p>You’ll need to cook a few pizzas and get used to the temperature of the oven, and to get a feel for how to adjust your flame while cooking. Once you’ve got it down, though, you can cook a Neapolitan pizza in just a minute or two.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/oWB4cCGpNUQVvcxoftMxhm.jpg" alt="A slice of pizza in hand, the pizza was cooked on the Gozney Arc XL" /><figcaption><small role="credit">Peter Wolinski / Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EBQYdvgx7xoLoEySJVdxsn.jpg" alt="A pizza cooked on the Gozney Arc XL" /><figcaption><small role="credit">Peter Wolinski / Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/yMwi7zeD3wrkj7PrzjXqun.jpg" alt="A pizza cooked on the Gozney Arc XL" /><figcaption><small role="credit">Peter Wolinski / Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ae59Fbbpv3Jk3hP3w9pNun.jpg" alt="A pizza cooked on the Gozney Arc XL" /><figcaption><small role="credit">Peter Wolinski / Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LGHQdEdiGemfaSrK9D2jun.jpg" alt="A pizza cooked on the Gozney Arc XL" /><figcaption><small role="credit">Peter Wolinski / Future</small></figcaption></figure></figure><p>I cooked a variety of pizzas, from 12-inch through to 16-inch bases, in Neapolitan and NY styles. On several occasions, I cooked for groups of six. The Arc XL handled it all beautifully. Thanks to the large 16-inch stone, you can cook bigger pizzas to feed multiple people, so you don’t need to spend the entirety of a pizza party churning out 12-inch pies for each person.</p><p>It’s very easy to burn larger 16-inch pies, as they sit so close to the flame. This means that you’ll want to turn them early, which can cause rips in the dough and an awful mess. This happened on a few occasions for me — to avoid this, ensure the stone is at least 750F / 400C before launching, so pizza bases cook very quickly.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4770px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LkKuqvfb2h4c9TcEEg9Ctn" name="Gozney Arc XL_Test shot-4" alt="A garlic flatbread cooked on the Gozney Arc XL" src="https://cdn.mos.cms.futurecdn.net/LkKuqvfb2h4c9TcEEg9Ctn.jpg" mos="" align="middle" fullscreen="" width="4770" height="2683" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>I also cooked large garlic flatbreads, as well as sides like charred broccoli spears and caramelized roasted carrots. The stone also gets hot enough to sear steaks and meat, so it’s a highly versatile oven.</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-storage-maintenance"><span>Gozney Arc XL review: Storage & maintenance</span></h2><p>My one major gripe with the Arc XL is how difficult the stone is to remove. Without buying a removal kit, you’ll need to tip the oven on its side and support the stone out — a two-person job, which also involves removing the oven from any stand or base you have it fastened to. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="cQ5L2r3DpjfDFQPvDFJF6h" name="Gozney Arc XL-6" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/cQ5L2r3DpjfDFQPvDFJF6h.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>This is infuriating if you make a mess. Gonzey’s advice is to burn the oven for 3 hours to clean it (wasting gas), which works, but only on the middle and rear of the stone. The front never gets hot enough to self-clean, so if you make a mess there when launching or removing a failed launch, you can’t clean it properly without a lot of hassle. You can scrape most mess off when cool, but not everything. And besides, food still gets stuck between the stone and the oven lip.</p><p>I love that Ooni’s stones are much easier to remove, so you can take them out and properly clean them up. As I mentioned before, this also means you don’t need to waste hours of gas just to clean. Gozney should include a stone removal kit with every oven.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="JovgEojWbc9cun2NkppDth" name="Gozney Arc XL-3" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/JovgEojWbc9cun2NkppDth.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Gozney also recommends burning the oven for several hours to clean soot from the roof, which is just ridiculously wasteful. I just waited for it to cool and cleaned with a dry cloth. Maintenance is otherwise very simple — just wipe down the exterior parts when cool.</p><p>Storage is as you’d expect for such a huge cooker: you’ll need a fair bit of space. I’d recommend <a href="https://us.gozney.com/products/arc-arc-xl-stand" target="_blank" rel="nofollow">Gozney’s Arc + Arc XL stand</a> ($299), which features fold-away ledges, storage hooks, and wheels to move the oven to and from position. It’s also important to cover the oven and keep it safe from the elements — Gozney sells covers for this (including a <a href="https://us.gozney.com/products/arc-arc-xl-stand-cover" target="_blank" rel="nofollow">cover that goes over the whole stand</a>).</p><h2 class="article-body__section" id="section-gozney-arc-xl-review-verdict"><span>Gozney Arc XL review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="diNPFJT9L76PJjBZeYjrJh" name="Gozney Arc XL-2" alt="A photo of the Gozney Arc XL in a garden" src="https://cdn.mos.cms.futurecdn.net/diNPFJT9L76PJjBZeYjrJh.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Gozney Arc XL is a fantastic pizza oven. With its large 16-inch stone, you can cook big pizzas for multiple people and simplify your pizza parties. It’s easy to use, beautiful to behold and built to last. And most importantly, it makes fantastic pizza.</p><p>There are a couple of things to consider. First, it’s pricey — you can get the rival Ooni Karu 16 for much less money, which is better than the Arc XL in almost every way. Secondly, the stone is tricky to remove, which makes the Arc XL trickier to clean than Ooni ovens. </p><p>If you’re willing to overlook those flaws, though, as I have, then I’ve no hesitation in recommending the Arc XL to small commercial and home pizzaioli. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ $300 vs $2,000 espresso machine — I ran a blind taste test to see if you need to spend $$$ for barista coffee at home ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Very little comes close to the satisfaction I feel when I take my first sip of an expensive flat white from an equally expensive independent cafe. I love everything about frequenting coffeehouses: the adventure of actually going outside, deciding which of the plentiful bean varietals I fancy, then scrutinizing — ahem, <em>watching</em> — the barista as they make my oat milk flat white. </p><p>But patronizing cafes is costly, and I know I'm not the only one to say I don't have the money for it. But even if you want to make cafe-quality coffee at home, there's a catch. Many home espresso machines carry a staggering initial cost. Even the best-value stalwarts, like the <a href="https://www.tomsguide.com/home/coffee-makers/delonghi-la-specialista-arte-evo-review">De'Longhi La Specialista Arte Evo</a> ($699) and my personal machine, the <a href="https://www.tomsguide.com/home/home-appliances/breville-sage-bambino-plus-review">Breville Bambino Plus</a> ($499), are objectively expensive. And don't even get me started on grinders. </p><p>But could you get away with a cheaper espresso machine, and instead invest in a grinder and high-quality beans? To find out, I pitted two of my favorite espresso machines against one another in a blind taste test: the gorgeous prosumer <a href="https://www.tomsguide.com/home/coffee-makers/lelit-mara-x3-review">Lelit MaraX3</a> ($1,999) and the cheap <a href="https://www.tomsguide.com/home/coffee-makers/sage-breville-bambino-review">Breville Bambino</a> ($299). </p><p>So which machine won — and which machine should you buy?</p><h2 class="article-body__section" id="section-the-competitors"><span>The competitors</span></h2>        <div class="featured_product_block featured_block_versus" data-id="5495463a-8a95-11f1-8df2-3be1201f93a3">            <a href="https://www.amazon.com/Breville-Bambino-Espresso-Machine-Stainless/dp/B0B1JPPG2L" data-model-name="Sage Bambino" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/VFx8KQ3FReuyYgDviuN5hS.jpg" alt="the breville bambino espresso machine in silver with steam wand and drip tray visible"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                            <div class='featured__brand'>Breville</div>                    <div class="featured__title">Bambino</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>The Breville Bambino is, without a doubt, the best budget espresso machine you can buy. It has premium features like customizable pre-infusion and a PID controller for much cheaper than most of the competition.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_versus" data-id="549546b2-8a95-11f1-a176-878bfda703d5">            <a href="https://www.seattlecoffeegear.com/products/lelit-mara-x3-espresso-machine?variant=53213339648307" data-model-name="Lelit Mara X3" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/K72vsuo35nBPsGGzk4K8xU.webp" alt="Lelit, Mara X3"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                            <div class='featured__brand'>Lelit</div>                    <div class="featured__title">MaraX3</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>The Lelit MaraX3 is the newest entry-level prosumer model by the Australian manufacturer Breville, designed and manufactured in Italy, combining a dual-boiler system, customizable pressure paddle, a live manometer, and a 58mm portafilter. You've even got real wood finishes.</p></p>                </div>                            </div>        </div><h2 class="article-body__section" id="section-the-control"><span>The control</span></h2><p>I used the same beans — <a href="https://cartercoffee.uk/products/mendoza-peru-anaerobic-nat-copy" target="_blank" rel="nofollow">Carter Coffee's Two Trees Geisha</a>, £21 ($28) for 200g. With tasting notes of passion fruit, white peach, and an aromatic finish, I knew my espresso testers were in for a treat. When I first opened the bag, it made my brain light up like I was seeing my long-lost dog for the first time in thirty years. </p><p>I ground the beans using my <a href="https://www.tomsguide.com/home/coffee-makers/mazzer-philos-review">Mazzer Philos</a> ($1,500), the best grinder in the world. I'll acknowledge that this grinder is blindingly expensive, and not reasonable for most people at all. You do <em>not</em> need to buy this grinder to achieve similar results. I would recommend the <a href="https://www.tomsguide.com/home/home-appliances/baratza-encore-esp-review#section-baratza-encore-esp-review-performance">Baratza Encore ESP</a> ($199) or <a href="https://www.tomsguide.com/home/home-appliances/comandante-c40-mk4-review">Comandante C40 MK4</a> (usually around $300) for most people. </p><p>A brief disclaimer: I use the Mazzer Philos in all my espresso experiments, so if any issues arise, I <em>know</em> it wasn't the grinder.</p><p>Admittedly, I ground the beans to slightly different sizes: click 22 for the Lelit, and click 25 for the Breville. This is because anything under click 25 resulted in an overextracted shot on the Breville, and anything over click 22 resulted in an underextracted shot on the Lelit. I dialed in my beans for each machine before commencing.</p><h2 class="article-body__section" id="section-the-test"><span>The test</span></h2><p>I ran three blind taste tests: Black Americano, oat milk flat white, and pure espresso. </p><h2 id="black-americano">Black Americano</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4574px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="bL9oJxo4z7fjCxHhFGNHPc" name="bambino vs mara x3" alt="a photo of a black americano made on the breville bambino and lelit mara x3" src="https://cdn.mos.cms.futurecdn.net/bL9oJxo4z7fjCxHhFGNHPc.png" mos="" align="middle" fullscreen="" width="4574" height="2573" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Can you guess which is which? (Coffee B on your left, and Coffee A on your right — confusing, I know. Sorry!) </span><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>I made the black Americanos identically: 18g of coffee for 36g out between 25-28 seconds. Topped up with 8 ounces of thirty-seconds-off-the-boil water.</p><p>For now, we'll call them <strong>Coffee A </strong>and <strong>Coffee B</strong>.<strong> </strong></p><p>My colleague Pete said <strong>Coffee A</strong> tasted "sour and bitter with less nuance, but still delicious." Lucy said <strong>A</strong> didn't have "much flavor," which was supported by James and Caspar, who said <strong>A</strong> tasted "a little thin" and "quite watery," respectively. </p><p>Josh was the only tester to prefer <strong>Coffee</strong> <strong>A</strong>: he reported it had a "richer aftertaste" and "more flavor in the afternotes" than <strong>Coffee B</strong>. </p><p>Lucy said <strong>Coffee B </strong>had a "more nuanced flavor than <strong>A,</strong>" which was supported by James: "tastes a lot fuller and stronger," and Caspar, who enjoyed <strong>B</strong>'s smoothness. Pete noticed the crema was thicker on <strong>Coffee B</strong>, which he thought meant it had a higher-quality group head. He preferred<strong> B </strong>overall due to the balanced bitter, caramel, and sour notes. </p><p>Josh was the only person to criticize <strong>B</strong>: he said it tasted "slightly more muted" and chose <strong>Coffee A</strong> as his favorite. </p><p>Can you guess which was which? </p><h2 id="oat-milk-flat-white">Oat milk flat white</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4418px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gfQQW7XvBvsedj6nZ4rpe7" name="bambino and mara flat white" alt="two flat whites made on the breville bambino and lelit mara x3" src="https://cdn.mos.cms.futurecdn.net/gfQQW7XvBvsedj6nZ4rpe7.png" mos="" align="middle" fullscreen="" width="4418" height="2485" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Can you guess which is which? Coffee B on your left, and Coffee A on your right. Confusing — sorry (again)! </span><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>The second test: oat milk flat white. I had a few fewer testers due to other responsibilities for this one. </p><p>Again, I made the coffees identically: 18g of coffee for 36g out within 25-28 seconds. I steamed milk using my usual barista-taught method (I am an ex-barista, after all). </p><p>Let's give them their names: <strong>Coffee A </strong>and <strong>Coffee B</strong>.</p><p>Pete preferred <strong>Coffee A</strong>, saying, "Milk is better textured on A. It's less aerated; smoother and silkier." He also preferred the coffee flavor on A. "Slight bit more flavor poking through. But very difficult to tell." </p><p>James said <strong>Coffee A </strong>was "really nice," but he liked the "darker, more intense flavor" on <strong>Coffee B </strong>more, so that was his favorite. Josh agreed with James, choosing <strong>Coffee B</strong> due to its "smoother" and "more coffee-like" flavor. </p><h2 id="espresso-2">Espresso</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2340px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="4S8Vvtr3M23SSgCY5jyxuY" name="bambino and mara x3 espresso" alt="two shots of espresso one made on lelit mara x3 one on the breville bambino" src="https://cdn.mos.cms.futurecdn.net/4S8Vvtr3M23SSgCY5jyxuY.png" mos="" align="middle" fullscreen="" width="2340" height="1316" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Can you tell which is which? Coffee A is on the right and Coffee B on the left (sorry — for the third and final time). </span><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>Now for the most important test: straight espresso. There's nowhere to hide here. </p><p>I actually made a single espresso for this one, using 12g of coffee for 24g of espresso out. I don't have two espresso glasses, so apologies for the unconventional container in the photographs above. </p><p>Again, I'll introduce the players: <strong>Coffee A </strong>and <strong>Coffee B</strong>. </p><p>Pete said <strong>Coffee A</strong> had a better crema and smelled "more potently of chocolate." When he took a sip, he said, "Oh, that's good." Not really much else for me to say, is there? Overall, he said <strong>Coffee A</strong> had "a lot of body and a bit of acidity." Spoiler alert, but after drinking <strong>Coffee B</strong>, he said <strong>Coffee A </strong>was, and I quote, "So much nicer."</p><p>James and Caspar also preferred <strong>Coffee A</strong>; James said it had "a lot more going on, with more levels to the coffee flavor."  </p><p>Josh mostly agreed — he said <strong>Coffee A</strong> had more fruit notes with a richer mouthfeel. I'll note here that Josh had the most sips out of anyone and mulled on his decision for much longer than anyone else, so his decision was almost too close to call.</p><p>Lucy was the only outlier, saying <strong>Coffee A</strong> had a "nice" espresso taste but not as rich as <strong>Coffee B</strong>, which had a more diverse flavor, with more dynamism in the sip. Interestingly, I also agreed — is this female vs male taste buds in action? (I'm joking — obviously there's no difference in female vs male taste buds).</p><p>What do you think? Ready for the results? </p><h2 class="article-body__section" id="section-the-results"><span>The results</span></h2><div ><table><thead><tr><th class="firstcol " ><p>Taster</p></th><th  ><p>Americano</p></th><th  ><p>Flat white</p></th><th  ><p>Espresso</p></th></tr></thead><tbody><tr><td class="firstcol " ><p>Pete</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>Coffee A - Lelit</p></td><td  ><p>Coffee A - Bambino</p></td></tr><tr><td class="firstcol " ><p>Josh</p></td><td  ><p>Coffee A - Lelit</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>Coffee A - Bambino</p></td></tr><tr><td class="firstcol " ><p>James</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>Coffee A - Bambino</p></td></tr><tr><td class="firstcol " ><p>Caspar</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>-</p></td><td  ><p>Coffee A - Bambino</p></td></tr><tr><td class="firstcol " ><p>Lucy</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>-</p></td><td  ><p>Coffee B - Lelit</p></td></tr><tr><td class="firstcol " ><p>Winner</p></td><td  ><p>Bambino</p></td><td  ><p>Bambino</p></td><td  ><p>Bambino</p></td></tr></tbody></table></div><p>And now I get to crown the $299 machine the winner! I didn't expect the Bambino to win — and neither did any of my testers. They were all shocked when I told them.</p><p>Of course, I tried all the drinks before I offered them to my taste-testers... and I preferred the Lelit MaraX3 every time. But is that because <em>I </em>knew it was the more expensive one, therefore I assumed it should have been tastier? Or because I personally prefer an acidic coffee, so this appealed to my unique taste buds?  </p><p>I suppose I'll never know. </p><h2 class="article-body__section" id="section-but-taste-isn-t-the-only-thing-you-should-consider"><span>But taste isn't the only thing you should consider</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="yRWDPrYTJUqnrzGTyAvGyZ" name="Sage_bambino_.JPG" alt="the breville bambino / sage bambino in silver photographed against a blue background with a wooden feature wall, showing the steam wand, group head, water tank, button controls, and espresso" src="https://cdn.mos.cms.futurecdn.net/yRWDPrYTJUqnrzGTyAvGyZ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Although I love the Breville Bambino — it's my favorite budget espresso machine, and there's literally zero competition — winning the taste test doesn't mean it's the <a href="https://www.tomsguide.com/home/coffee-makers/im-an-ex-barista-heres-my-top-3-espresso-machines-for-every-budget">best espresso machine</a> in every situation. </p><p>The MaraX3 and Bambino are (mostly) intended for a different audience. You're not just paying $2,000 for coffee. You're paying $2,000 for a dual boiler, meaning you can steam milk and pull shots at the same time. You're paying $2,000 for the all-metal (+wood) construction, which might last longer than the Bambino's plastic. You're paying $2,000 for the steam, milk, and pressure configuration tools. </p><p>We're all thinking it, so I might as well say it: You're paying $2,000 for the Lelit branding and the Lelit design. </p><p>The MaraX3 is a work of art, and you should treat it like one. </p><p>If I had $2k, I know which machine I'd choose. The Lelit would already be in my kitchen.</p><p>Despite all of this, though, Lelit and Breville are part of the same company. Lelit is literally Breville's prosumer brand. The Baratza Encore ESP grinder I mentioned earlier? Also owned by Breville. </p><p>So, at the end of the day, your money's going to the same pocket. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1820px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="Unvedt68K7JGoD2qj6ro63" name="Lelit_Mara_X320.JPG" alt="the lelit mara x3 in white and walnut photographed against a blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Unvedt68K7JGoD2qj6ro63.jpg" mos="" align="middle" fullscreen="" width="1820" height="1024" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you want more coffee content, don't fret. I've got bucketloads (cuploads?). Every week, I release new episodes of <a href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, my series dedicated to forgetting coffee snobbery and figuring out what actually matters in your daily brew. </p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-the-new-nespresso-summer-flavors-heres-my-favorite"><strong>I tried the new Nespresso summer flavors — here's my favorite</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/non-plastic-coffee-makers-are-trending-and-this-low-plastic-ratio-model-is-my-favorite-for-coffee-snobs"><strong>Non-plastic coffee makers are trending — and this low-plastic Ratio model is my favorite for coffee snobs</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/usd300-vs-usd2-000-espresso-machine-i-ran-a-blind-taste-test-to-see-if-you-need-to-spend-usd-usd-usd-for-barista-coffee-at-home</link>
                                                                            <description>
                            <![CDATA[ I ran a blind taste test to answer the age-old question: Does a more expensive espresso machine make better coffee? ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">KdYVZFAVcHcRjWVLqLNuG9</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/gj6E9VE3uZhnKh7qz2MQvJ-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Wed, 29 Jul 2026 08:00:00 +0000</pubDate>                                                                                                                                <updated>Mon, 03 Aug 2026 08:35:00 +0000</updated>
                                                                                                                                            <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/png" url="https://cdn.mos.cms.futurecdn.net/gj6E9VE3uZhnKh7qz2MQvJ-1280-80.png">
                                                            <media:credit><![CDATA[Erin Bashford]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the breville bambino and lelit mara x3 photographed in a kitchen set with specialty coffee in the foreground]]></media:description>                                                            <media:text><![CDATA[the breville bambino and lelit mara x3 photographed in a kitchen set with specialty coffee in the foreground]]></media:text>
                                <media:title type="plain"><![CDATA[the breville bambino and lelit mara x3 photographed in a kitchen set with specialty coffee in the foreground]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/gj6E9VE3uZhnKh7qz2MQvJ-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Very little comes close to the satisfaction I feel when I take my first sip of an expensive flat white from an equally expensive independent cafe. I love everything about frequenting coffeehouses: the adventure of actually going outside, deciding which of the plentiful bean varietals I fancy, then scrutinizing — ahem, <em>watching</em> — the barista as they make my oat milk flat white. </p><p>But patronizing cafes is costly, and I know I'm not the only one to say I don't have the money for it. But even if you want to make cafe-quality coffee at home, there's a catch. Many home espresso machines carry a staggering initial cost. Even the best-value stalwarts, like the <a href="https://www.tomsguide.com/home/coffee-makers/delonghi-la-specialista-arte-evo-review">De'Longhi La Specialista Arte Evo</a> ($699) and my personal machine, the <a href="https://www.tomsguide.com/home/home-appliances/breville-sage-bambino-plus-review">Breville Bambino Plus</a> ($499), are objectively expensive. And don't even get me started on grinders. </p><p>But could you get away with a cheaper espresso machine, and instead invest in a grinder and high-quality beans? To find out, I pitted two of my favorite espresso machines against one another in a blind taste test: the gorgeous prosumer <a href="https://www.tomsguide.com/home/coffee-makers/lelit-mara-x3-review">Lelit MaraX3</a> ($1,999) and the cheap <a href="https://www.tomsguide.com/home/coffee-makers/sage-breville-bambino-review">Breville Bambino</a> ($299). </p><p>So which machine won — and which machine should you buy?</p><h2 class="article-body__section" id="section-the-competitors"><span>The competitors</span></h2>        <div class="featured_product_block featured_block_versus" data-id="5495463a-8a95-11f1-8df2-3be1201f93a3">            <a href="https://www.amazon.com/Breville-Bambino-Espresso-Machine-Stainless/dp/B0B1JPPG2L" data-model-name="Sage Bambino" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/VFx8KQ3FReuyYgDviuN5hS.jpg" alt="the breville bambino espresso machine in silver with steam wand and drip tray visible"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                            <div class='featured__brand'>Breville</div>                    <div class="featured__title">Bambino</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>The Breville Bambino is, without a doubt, the best budget espresso machine you can buy. It has premium features like customizable pre-infusion and a PID controller for much cheaper than most of the competition.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_versus" data-id="549546b2-8a95-11f1-a176-878bfda703d5">            <a href="https://www.seattlecoffeegear.com/products/lelit-mara-x3-espresso-machine?variant=53213339648307" data-model-name="Lelit Mara X3" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/K72vsuo35nBPsGGzk4K8xU.webp" alt="Lelit, Mara X3"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                            <div class='featured__brand'>Lelit</div>                    <div class="featured__title">MaraX3</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>The Lelit MaraX3 is the newest entry-level prosumer model by the Australian manufacturer Breville, designed and manufactured in Italy, combining a dual-boiler system, customizable pressure paddle, a live manometer, and a 58mm portafilter. You've even got real wood finishes.</p></p>                </div>                            </div>        </div><h2 class="article-body__section" id="section-the-control"><span>The control</span></h2><p>I used the same beans — <a href="https://cartercoffee.uk/products/mendoza-peru-anaerobic-nat-copy" target="_blank" rel="nofollow">Carter Coffee's Two Trees Geisha</a>, £21 ($28) for 200g. With tasting notes of passion fruit, white peach, and an aromatic finish, I knew my espresso testers were in for a treat. When I first opened the bag, it made my brain light up like I was seeing my long-lost dog for the first time in thirty years. </p><p>I ground the beans using my <a href="https://www.tomsguide.com/home/coffee-makers/mazzer-philos-review">Mazzer Philos</a> ($1,500), the best grinder in the world. I'll acknowledge that this grinder is blindingly expensive, and not reasonable for most people at all. You do <em>not</em> need to buy this grinder to achieve similar results. I would recommend the <a href="https://www.tomsguide.com/home/home-appliances/baratza-encore-esp-review#section-baratza-encore-esp-review-performance">Baratza Encore ESP</a> ($199) or <a href="https://www.tomsguide.com/home/home-appliances/comandante-c40-mk4-review">Comandante C40 MK4</a> (usually around $300) for most people. </p><p>A brief disclaimer: I use the Mazzer Philos in all my espresso experiments, so if any issues arise, I <em>know</em> it wasn't the grinder.</p><p>Admittedly, I ground the beans to slightly different sizes: click 22 for the Lelit, and click 25 for the Breville. This is because anything under click 25 resulted in an overextracted shot on the Breville, and anything over click 22 resulted in an underextracted shot on the Lelit. I dialed in my beans for each machine before commencing.</p><h2 class="article-body__section" id="section-the-test"><span>The test</span></h2><p>I ran three blind taste tests: Black Americano, oat milk flat white, and pure espresso. </p><h2 id="black-americano">Black Americano</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4574px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="bL9oJxo4z7fjCxHhFGNHPc" name="bambino vs mara x3" alt="a photo of a black americano made on the breville bambino and lelit mara x3" src="https://cdn.mos.cms.futurecdn.net/bL9oJxo4z7fjCxHhFGNHPc.png" mos="" align="middle" fullscreen="" width="4574" height="2573" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Can you guess which is which? (Coffee B on your left, and Coffee A on your right — confusing, I know. Sorry!) </span><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>I made the black Americanos identically: 18g of coffee for 36g out between 25-28 seconds. Topped up with 8 ounces of thirty-seconds-off-the-boil water.</p><p>For now, we'll call them <strong>Coffee A </strong>and <strong>Coffee B</strong>.<strong> </strong></p><p>My colleague Pete said <strong>Coffee A</strong> tasted "sour and bitter with less nuance, but still delicious." Lucy said <strong>A</strong> didn't have "much flavor," which was supported by James and Caspar, who said <strong>A</strong> tasted "a little thin" and "quite watery," respectively. </p><p>Josh was the only tester to prefer <strong>Coffee</strong> <strong>A</strong>: he reported it had a "richer aftertaste" and "more flavor in the afternotes" than <strong>Coffee B</strong>. </p><p>Lucy said <strong>Coffee B </strong>had a "more nuanced flavor than <strong>A,</strong>" which was supported by James: "tastes a lot fuller and stronger," and Caspar, who enjoyed <strong>B</strong>'s smoothness. Pete noticed the crema was thicker on <strong>Coffee B</strong>, which he thought meant it had a higher-quality group head. He preferred<strong> B </strong>overall due to the balanced bitter, caramel, and sour notes. </p><p>Josh was the only person to criticize <strong>B</strong>: he said it tasted "slightly more muted" and chose <strong>Coffee A</strong> as his favorite. </p><p>Can you guess which was which? </p><h2 id="oat-milk-flat-white">Oat milk flat white</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4418px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gfQQW7XvBvsedj6nZ4rpe7" name="bambino and mara flat white" alt="two flat whites made on the breville bambino and lelit mara x3" src="https://cdn.mos.cms.futurecdn.net/gfQQW7XvBvsedj6nZ4rpe7.png" mos="" align="middle" fullscreen="" width="4418" height="2485" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Can you guess which is which? Coffee B on your left, and Coffee A on your right. Confusing — sorry (again)! </span><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>The second test: oat milk flat white. I had a few fewer testers due to other responsibilities for this one. </p><p>Again, I made the coffees identically: 18g of coffee for 36g out within 25-28 seconds. I steamed milk using my usual barista-taught method (I am an ex-barista, after all). </p><p>Let's give them their names: <strong>Coffee A </strong>and <strong>Coffee B</strong>.</p><p>Pete preferred <strong>Coffee A</strong>, saying, "Milk is better textured on A. It's less aerated; smoother and silkier." He also preferred the coffee flavor on A. "Slight bit more flavor poking through. But very difficult to tell." </p><p>James said <strong>Coffee A </strong>was "really nice," but he liked the "darker, more intense flavor" on <strong>Coffee B </strong>more, so that was his favorite. Josh agreed with James, choosing <strong>Coffee B</strong> due to its "smoother" and "more coffee-like" flavor. </p><h2 id="espresso-2">Espresso</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2340px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="4S8Vvtr3M23SSgCY5jyxuY" name="bambino and mara x3 espresso" alt="two shots of espresso one made on lelit mara x3 one on the breville bambino" src="https://cdn.mos.cms.futurecdn.net/4S8Vvtr3M23SSgCY5jyxuY.png" mos="" align="middle" fullscreen="" width="2340" height="1316" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Can you tell which is which? Coffee A is on the right and Coffee B on the left (sorry — for the third and final time). </span><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>Now for the most important test: straight espresso. There's nowhere to hide here. </p><p>I actually made a single espresso for this one, using 12g of coffee for 24g of espresso out. I don't have two espresso glasses, so apologies for the unconventional container in the photographs above. </p><p>Again, I'll introduce the players: <strong>Coffee A </strong>and <strong>Coffee B</strong>. </p><p>Pete said <strong>Coffee A</strong> had a better crema and smelled "more potently of chocolate." When he took a sip, he said, "Oh, that's good." Not really much else for me to say, is there? Overall, he said <strong>Coffee A</strong> had "a lot of body and a bit of acidity." Spoiler alert, but after drinking <strong>Coffee B</strong>, he said <strong>Coffee A </strong>was, and I quote, "So much nicer."</p><p>James and Caspar also preferred <strong>Coffee A</strong>; James said it had "a lot more going on, with more levels to the coffee flavor."  </p><p>Josh mostly agreed — he said <strong>Coffee A</strong> had more fruit notes with a richer mouthfeel. I'll note here that Josh had the most sips out of anyone and mulled on his decision for much longer than anyone else, so his decision was almost too close to call.</p><p>Lucy was the only outlier, saying <strong>Coffee A</strong> had a "nice" espresso taste but not as rich as <strong>Coffee B</strong>, which had a more diverse flavor, with more dynamism in the sip. Interestingly, I also agreed — is this female vs male taste buds in action? (I'm joking — obviously there's no difference in female vs male taste buds).</p><p>What do you think? Ready for the results? </p><h2 class="article-body__section" id="section-the-results"><span>The results</span></h2><div ><table><thead><tr><th class="firstcol " ><p>Taster</p></th><th  ><p>Americano</p></th><th  ><p>Flat white</p></th><th  ><p>Espresso</p></th></tr></thead><tbody><tr><td class="firstcol " ><p>Pete</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>Coffee A - Lelit</p></td><td  ><p>Coffee A - Bambino</p></td></tr><tr><td class="firstcol " ><p>Josh</p></td><td  ><p>Coffee A - Lelit</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>Coffee A - Bambino</p></td></tr><tr><td class="firstcol " ><p>James</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>Coffee A - Bambino</p></td></tr><tr><td class="firstcol " ><p>Caspar</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>-</p></td><td  ><p>Coffee A - Bambino</p></td></tr><tr><td class="firstcol " ><p>Lucy</p></td><td  ><p>Coffee B - Bambino</p></td><td  ><p>-</p></td><td  ><p>Coffee B - Lelit</p></td></tr><tr><td class="firstcol " ><p>Winner</p></td><td  ><p>Bambino</p></td><td  ><p>Bambino</p></td><td  ><p>Bambino</p></td></tr></tbody></table></div><p>And now I get to crown the $299 machine the winner! I didn't expect the Bambino to win — and neither did any of my testers. They were all shocked when I told them.</p><p>Of course, I tried all the drinks before I offered them to my taste-testers... and I preferred the Lelit MaraX3 every time. But is that because <em>I </em>knew it was the more expensive one, therefore I assumed it should have been tastier? Or because I personally prefer an acidic coffee, so this appealed to my unique taste buds?  </p><p>I suppose I'll never know. </p><h2 class="article-body__section" id="section-but-taste-isn-t-the-only-thing-you-should-consider"><span>But taste isn't the only thing you should consider</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="yRWDPrYTJUqnrzGTyAvGyZ" name="Sage_bambino_.JPG" alt="the breville bambino / sage bambino in silver photographed against a blue background with a wooden feature wall, showing the steam wand, group head, water tank, button controls, and espresso" src="https://cdn.mos.cms.futurecdn.net/yRWDPrYTJUqnrzGTyAvGyZ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Although I love the Breville Bambino — it's my favorite budget espresso machine, and there's literally zero competition — winning the taste test doesn't mean it's the <a href="https://www.tomsguide.com/home/coffee-makers/im-an-ex-barista-heres-my-top-3-espresso-machines-for-every-budget">best espresso machine</a> in every situation. </p><p>The MaraX3 and Bambino are (mostly) intended for a different audience. You're not just paying $2,000 for coffee. You're paying $2,000 for a dual boiler, meaning you can steam milk and pull shots at the same time. You're paying $2,000 for the all-metal (+wood) construction, which might last longer than the Bambino's plastic. You're paying $2,000 for the steam, milk, and pressure configuration tools. </p><p>We're all thinking it, so I might as well say it: You're paying $2,000 for the Lelit branding and the Lelit design. </p><p>The MaraX3 is a work of art, and you should treat it like one. </p><p>If I had $2k, I know which machine I'd choose. The Lelit would already be in my kitchen.</p><p>Despite all of this, though, Lelit and Breville are part of the same company. Lelit is literally Breville's prosumer brand. The Baratza Encore ESP grinder I mentioned earlier? Also owned by Breville. </p><p>So, at the end of the day, your money's going to the same pocket. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1820px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="Unvedt68K7JGoD2qj6ro63" name="Lelit_Mara_X320.JPG" alt="the lelit mara x3 in white and walnut photographed against a blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Unvedt68K7JGoD2qj6ro63.jpg" mos="" align="middle" fullscreen="" width="1820" height="1024" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you want more coffee content, don't fret. I've got bucketloads (cuploads?). Every week, I release new episodes of <a href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, my series dedicated to forgetting coffee snobbery and figuring out what actually matters in your daily brew. </p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-the-new-nespresso-summer-flavors-heres-my-favorite"><strong>I tried the new Nespresso summer flavors — here's my favorite</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/non-plastic-coffee-makers-are-trending-and-this-low-plastic-ratio-model-is-my-favorite-for-coffee-snobs"><strong>Non-plastic coffee makers are trending — and this low-plastic Ratio model is my favorite for coffee snobs</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I mixed the exact same smoothie in a $30 blender and a $129 one — here's my verdict ]]></title>
                                                                                                <dc:content><![CDATA[ <p>At the start of the year, I <a href="https://www.tomsguide.com/home/kitchen-dining/i-upgraded-my-usd30-budget-blender-to-a-nutribullet-and-its-made-smoothies-fun-again">upgraded our household blender</a> from a regular non-brand contraption we picked up at the local Aldi to a <a href="https://www.tomsguide.com/home/home-appliances/nutribullet-ultra-1200-personal-blender-review">Nutribullet Ultra 1200. </a>The benefits were that it was easier to clean, looked a lot nicer, and blends your ingredients straight into the shaker you can drink from.</p><p>Over six months later, I decided to return to the pair of them (we haven't got rid of the old one; it's good to have a spare) to put a little bit more testing on the table.</p><p>Part of this, I admit, is due to selfishly wanting to mix up my smoothie game. I'm guilty of going back to the same old recipe time and time again and, with summer here, I'm feeling the need for a change. </p><p>Secondly, although I swapped to the Nutribullet without a second thought — was I giving my old blender its due? What if I fed both machines the <em>exact same ingredients</em> and measured them directly in a head-to-head? Which would really come out on top?</p><p>There are a ton of things the Nutribullet does better: it's objectively smaller, lighter, simpler to use <em>and</em> comes with a better warranty. But I was surprised at a couple of outcomes from this experiment if I'm being totally honest. I'll spoil the big one for you now: <strong>I couldn't tell the difference when it came to tasting the final product</strong>.</p><p>But we're getting ahead of ourselves. Read on to discover just what I found out from my budget vs. premium experiment.</p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7667954816726895886" data-video-id="7667954816726895886" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ miffy cafe - sakuracloud" href="https://www.tiktok.com/music/miffy-cafe-7425305480727889936">♬ miffy cafe - sakuracloud</a></section>                    </blockquote></div>                <h3 class="article-body__section" id="section-the-recipe"><span>The recipe</span></h3><p>The wonderful thing about smoothies is they're uncomplicated. Pick stuff you like, throw it inside one of <a href="https://www.tomsguide.com/best-picks/best-blenders">the best blenders</a>, and chances are it'll come out alright. But this test had to be scientific, so here are the exact ingredients I used with both machines:</p><p><strong>100g strawberries </strong>(~1 cup, yields a thick texture and rich color)</p><p><strong>50g ripe banana</strong> (~1/2 medium banana, sliced)</p><p><strong>100g strawberry flavored Greek yogurt</strong> (~½ cup)</p><p><strong>180ml milk</strong> (~¾ cup)</p><p><strong>1 tbsp of honey</strong></p><p>Note that both of these machines are blenders and not bespoke juicers. Here's a <a href="https://www.tomsguide.com/news/juicer-vs-blender-what-is-the-differnce">complete rundown of the differences,</a> but in a nutshell, juicers squeeze and extract the juice from fruits and vegetables. A juicer breaks down the fruit by separating the pulp, peel/skin, and seeds from the fruit, as the juice flows out through a sieve. </p><p>Blenders, on the other hand, pulverize all of the ingredients together. Think of them as a sledgehammer rather than a scalpel.</p><h3 class="article-body__section" id="section-the-blend"><span>The blend</span></h3><p>Forget just throwing the above ingredients into the pot without a care. This is the process I followed with both blenders — just to make sure I did what I could to make the experiment fair.</p><ul><li><strong>Add liquids and soft ingredients first: </strong>Pour in the <strong>milk</strong> and spoon in the <strong>Greek yogurt</strong>. Placing liquid at the bottom helps the blender blades turn smoothly without getting stuck on the fruit.</li><li><strong>Layer in solids and the fruit: Next in went the</strong> <strong>banana slices</strong>, chopped <strong>strawberries</strong>, and finally I added the honey.</li><li><strong>Blend on high power for 30 seconds </strong>(I used my trusty Casio stopwatch to make sure they each got the same time)</li><li>Pour out and enjoy</li></ul><h3 class="article-body__section" id="section-the-result"><span>The result</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5rUCzNkPzywGJQtUU5KPJG" name="IMG_4627.JPG" alt="Two blenders with a glass each of smoothie" src="https://cdn.mos.cms.futurecdn.net/5rUCzNkPzywGJQtUU5KPJG.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Not pictured: All the washing up that needed to be done after this experiment was complete </span><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide / Jeff Parsons)</span></figcaption></figure><p>I appreciate you can't <em>taste</em> the results of my experiment through the screen, but as I said at the outset. I didn't notice any difference in taste in the final product. If pressed, I would say the $129 result was a fractionally smoother than the cheaper alternative. Maybe my smoothie chops aren't what they used to be, but my takeaway is that if all you're interested in is the consistency and the taste of the final result, the cheap option will do you proud.</p><p>Also despite costing more and supposedly being quieter (Nutribullet says 87dB), the more expensive blender was louder than the budget one. It's more powerful 1,200W vs 600W and that showed when the noise level just crested 100dB for the Nutribullet compared to 99dB for the Blaupunkt. I know this is nitpicking, but I still wanted to mention it.</p><p>The Nutribullet uses a 6-point titanium-coated extractor blade rather than a 4-point one in the $30 Blaupunkt. But it's also got a lower capacity (32oz vs 52oz), so if you're mixing smoothies for a family or to batch, that may play a factor. Those two elements come in clutch because there were definitely some lumps left in the bottom of the jug on the cheaper blender when I poured out the drink.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4096px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ekPC5sugsCFx3xYSw2WPsF" name="Nutribullet Ultra 1200" alt="Nutribullet Ultra 1200 vs budget blender" src="https://cdn.mos.cms.futurecdn.net/ekPC5sugsCFx3xYSw2WPsF.jpg" mos="" align="middle" fullscreen="" width="4096" height="2304" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Whereas I put the contents into a glass for the sake of this article, normally I would just cap off the Nutribullet shaker and be on my way. Similarly, I cranked the Blaupunkt all the way to five on its antiquated knob rather than the simple on/off capacitive touch of the Nutribullet. </p><p>And, last but by no means least, cleaning the Nutribullet was a simpler, quicker affair than my old budget blender.</p><p>So, both got the job done — one just did it with a little more style than the other.</p><h3 class="article-body__section" id="section-final-thoughts"><span>Final thoughts</span></h3><div class="product"><a data-dimension112="e59b75e6-8676-11f1-9acc-718a67d38a99" data-action="Deal Block" data-label="Ultra" data-dimension48="Ultra" data-dimension25="$124" href="https://www.amazon.com/gp/aw/d/B0CBWD3PN7" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1546px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="X2G4ud3wXRFEsYCZi8Hs9C" name="Nutribullet ultra" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/X2G4ud3wXRFEsYCZi8Hs9C.jpg" mos="" align="middle" fullscreen="" width="1546" height="1546" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Nutribullet Ultra 1200 is a powerhouse of a blender boasting a 1200-watt motor, titanium-coated extractor blade and Tritan Renew cups (700ml and 900ml) that are both durable and sustainable. In Nutribullet's signature Gunmetal Grey, it's a sleek yet powerful combo that aims to deliver smoothies in just 30 seconds flat.<a class="view-deal button" href="https://www.amazon.com/gp/aw/d/B0CBWD3PN7" target="_blank" rel="nofollow" data-dimension112="e59b75e6-8676-11f1-9acc-718a67d38a99" data-action="Deal Block" data-label="Ultra" data-dimension48="Ultra" data-dimension25="$124">View Deal</a></p></div><div class="product"><a data-dimension112="e59b7640-8676-11f1-9b53-dd2c078d49e7" data-action="Deal Block" data-label="Ultra" data-dimension48="Ultra" data-dimension25="£129" href="https://www.amazon.co.uk/nutribullet-Ultra-1200W-Personal-Blender/dp/B0CSDWRT8N" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1546px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="X2G4ud3wXRFEsYCZi8Hs9C" name="Nutribullet ultra" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/X2G4ud3wXRFEsYCZi8Hs9C.jpg" mos="" align="middle" fullscreen="" width="1546" height="1546" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/nutribullet-Ultra-1200W-Personal-Blender/dp/B0CSDWRT8N" target="_blank" rel="nofollow" data-dimension112="e59b7640-8676-11f1-9b53-dd2c078d49e7" data-action="Deal Block" data-label="Ultra" data-dimension48="Ultra" data-dimension25="£129">Ultra: £129</a></strong><br>This product is also available in the U.K. where it's currently sitting at full price. During sales events, however, it can drop down to under £100 so it may be worth adding to your wishlist and keeping an eye on it.<a class="view-deal button" href="https://www.amazon.co.uk/nutribullet-Ultra-1200W-Personal-Blender/dp/B0CSDWRT8N" target="_blank" rel="nofollow" data-dimension112="e59b7640-8676-11f1-9b53-dd2c078d49e7" data-action="Deal Block" data-label="Ultra" data-dimension48="Ultra" data-dimension25="£129">View Deal</a></p></div><p>This little experiment has reinforced what I felt about leveling up blenders back at the start of the year. When it comes to the nuts and bolts of the taste of the final product, there's no difference — at least for someone with my unrefined palate.</p><p>Moreover, I can put up with a bit of noise for 30 seconds, so the fact my Nutribullet was louder than advertised is also something I can overlook.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4096px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="WLz7mEum8qLNUquUQUJUrF" name="Nutribullet Ultra 1200" alt="Nutribullet Ultra 1200 vs budget blender" src="https://cdn.mos.cms.futurecdn.net/WLz7mEum8qLNUquUQUJUrF.jpg" mos="" align="middle" fullscreen="" width="4096" height="2304" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>But, all the other aspects of using a blender were far superior with the more expensive model. I like that: <strong>a) it takes up less countertop space, b) it doesn't take as long to clean, c) it comes with a dedicated shaker for less fuss c) it genuinely just looks cooler and d) if it breaks, I can claim on the warranty for longer.</strong></p><p>Are those advantages worth $100 more? For my current lifestyle: yes. If you'd have asked me years ago when I was a student watching every dime I spent, then probably no.</p><p>The lesson I learned here was that despite our constant fixation on the end result, there's more to choosing a product than what it delivers. Less destination, more journey. </p><p>Of course, what works for me might not work for you. I happen to value how easy and quick the Nutribullet is to use so that makes the extra $100 worth it. Do you agree or not? Let me know in the comments below.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XZKP2e"></div>                            </div>                            <script src="https://kwizly.com/embed/XZKP2e.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/news/masticating-vs-centrifugal-juicer-which-is-best">Masticating vs centrifugal juicer: Which is best?</a></li><li><a href="https://www.tomsguide.com/best-picks/best-blenders">This is the best blender in the world, and two trusty, cheaper alternatives</a></li><li><a href="https://www.tomsguide.com/news/juicer-vs-blender-what-is-the-differnce">Juicer vs Blender: What's the difference?</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/i-mixed-the-exact-same-smoothie-in-a-usd30-blender-and-a-usd129-one-i-learned-a-valuable-lesson-in-the-process</link>
                                                                            <description>
                            <![CDATA[ After upgrading my blender at the start of the year, I felt it was time for a head-to-head test on which was better. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">iZB5DiXnxdDdR3sBqhSfri</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/bosNuKcBn37ynDukz8DqZm-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 29 Jul 2026 05:30:00 +0000</pubDate>                                                                                                                                <updated>Wed, 29 Jul 2026 14:23:23 +0000</updated>
                                                                                                                                            <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ jeff.parsons@futurenet.com (Jeff Parsons) ]]></author>                    <dc:creator><![CDATA[ Jeff Parsons ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/7z3UTGGrmSokMKxTWHmhjX.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Jeff is U.K. Editor-in-Chief for Tom’s Guide looking after the day-to-day output of the site’s British contingent. &lt;/p&gt;&lt;p&gt;Rising early and heading straight for the coffee machine, Jeff loves nothing more than dialling into the zeitgeist of the day’s tech news. A journalist for over a decade, he&#039;s travelled around the world testing and reviewing any gadget he can get his hands on.&lt;/p&gt;&lt;p&gt;Before joining the team at Tom’s Guide, Jeff covered technology and science for two of the U.K.’s biggest national news sites: Metro.co.uk and the Daily Mirror. Memorable moments include getting lost in Vienna in an electric Audi, touring Lockheed Martin’s mile-long jet factory in Fort Worth and filming a Netflix documentary about Elon Musk in West London.&lt;/p&gt;&lt;p&gt;When not plugged into the current news agenda, editing or commissioning a series of articles or debating the merits of Apple vs Android, Jeff can usually be found out for a run trying to shave precious seconds off his PB. Or lifting weights in a vain attempt to offset the ageing process.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/bosNuKcBn37ynDukz8DqZm-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[I mixed the exact same smoothie in a $30 blender and a $129 one]]></media:description>                                                            <media:text><![CDATA[I mixed the exact same smoothie in a $30 blender and a $129 one]]></media:text>
                                <media:title type="plain"><![CDATA[I mixed the exact same smoothie in a $30 blender and a $129 one]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/bosNuKcBn37ynDukz8DqZm-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>At the start of the year, I <a href="https://www.tomsguide.com/home/kitchen-dining/i-upgraded-my-usd30-budget-blender-to-a-nutribullet-and-its-made-smoothies-fun-again">upgraded our household blender</a> from a regular non-brand contraption we picked up at the local Aldi to a <a href="https://www.tomsguide.com/home/home-appliances/nutribullet-ultra-1200-personal-blender-review">Nutribullet Ultra 1200. </a>The benefits were that it was easier to clean, looked a lot nicer, and blends your ingredients straight into the shaker you can drink from.</p><p>Over six months later, I decided to return to the pair of them (we haven't got rid of the old one; it's good to have a spare) to put a little bit more testing on the table.</p><p>Part of this, I admit, is due to selfishly wanting to mix up my smoothie game. I'm guilty of going back to the same old recipe time and time again and, with summer here, I'm feeling the need for a change. </p><p>Secondly, although I swapped to the Nutribullet without a second thought — was I giving my old blender its due? What if I fed both machines the <em>exact same ingredients</em> and measured them directly in a head-to-head? Which would really come out on top?</p><p>There are a ton of things the Nutribullet does better: it's objectively smaller, lighter, simpler to use <em>and</em> comes with a better warranty. But I was surprised at a couple of outcomes from this experiment if I'm being totally honest. I'll spoil the big one for you now: <strong>I couldn't tell the difference when it came to tasting the final product</strong>.</p><p>But we're getting ahead of ourselves. Read on to discover just what I found out from my budget vs. premium experiment.</p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7667954816726895886" data-video-id="7667954816726895886" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ miffy cafe - sakuracloud" href="https://www.tiktok.com/music/miffy-cafe-7425305480727889936">♬ miffy cafe - sakuracloud</a></section>                    </blockquote></div>                <h3 class="article-body__section" id="section-the-recipe"><span>The recipe</span></h3><p>The wonderful thing about smoothies is they're uncomplicated. Pick stuff you like, throw it inside one of <a href="https://www.tomsguide.com/best-picks/best-blenders">the best blenders</a>, and chances are it'll come out alright. But this test had to be scientific, so here are the exact ingredients I used with both machines:</p><p><strong>100g strawberries </strong>(~1 cup, yields a thick texture and rich color)</p><p><strong>50g ripe banana</strong> (~1/2 medium banana, sliced)</p><p><strong>100g strawberry flavored Greek yogurt</strong> (~½ cup)</p><p><strong>180ml milk</strong> (~¾ cup)</p><p><strong>1 tbsp of honey</strong></p><p>Note that both of these machines are blenders and not bespoke juicers. Here's a <a href="https://www.tomsguide.com/news/juicer-vs-blender-what-is-the-differnce">complete rundown of the differences,</a> but in a nutshell, juicers squeeze and extract the juice from fruits and vegetables. A juicer breaks down the fruit by separating the pulp, peel/skin, and seeds from the fruit, as the juice flows out through a sieve. </p><p>Blenders, on the other hand, pulverize all of the ingredients together. Think of them as a sledgehammer rather than a scalpel.</p><h3 class="article-body__section" id="section-the-blend"><span>The blend</span></h3><p>Forget just throwing the above ingredients into the pot without a care. This is the process I followed with both blenders — just to make sure I did what I could to make the experiment fair.</p><ul><li><strong>Add liquids and soft ingredients first: </strong>Pour in the <strong>milk</strong> and spoon in the <strong>Greek yogurt</strong>. Placing liquid at the bottom helps the blender blades turn smoothly without getting stuck on the fruit.</li><li><strong>Layer in solids and the fruit: Next in went the</strong> <strong>banana slices</strong>, chopped <strong>strawberries</strong>, and finally I added the honey.</li><li><strong>Blend on high power for 30 seconds </strong>(I used my trusty Casio stopwatch to make sure they each got the same time)</li><li>Pour out and enjoy</li></ul><h3 class="article-body__section" id="section-the-result"><span>The result</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5rUCzNkPzywGJQtUU5KPJG" name="IMG_4627.JPG" alt="Two blenders with a glass each of smoothie" src="https://cdn.mos.cms.futurecdn.net/5rUCzNkPzywGJQtUU5KPJG.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Not pictured: All the washing up that needed to be done after this experiment was complete </span><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide / Jeff Parsons)</span></figcaption></figure><p>I appreciate you can't <em>taste</em> the results of my experiment through the screen, but as I said at the outset. I didn't notice any difference in taste in the final product. If pressed, I would say the $129 result was a fractionally smoother than the cheaper alternative. Maybe my smoothie chops aren't what they used to be, but my takeaway is that if all you're interested in is the consistency and the taste of the final result, the cheap option will do you proud.</p><p>Also despite costing more and supposedly being quieter (Nutribullet says 87dB), the more expensive blender was louder than the budget one. It's more powerful 1,200W vs 600W and that showed when the noise level just crested 100dB for the Nutribullet compared to 99dB for the Blaupunkt. I know this is nitpicking, but I still wanted to mention it.</p><p>The Nutribullet uses a 6-point titanium-coated extractor blade rather than a 4-point one in the $30 Blaupunkt. But it's also got a lower capacity (32oz vs 52oz), so if you're mixing smoothies for a family or to batch, that may play a factor. Those two elements come in clutch because there were definitely some lumps left in the bottom of the jug on the cheaper blender when I poured out the drink.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4096px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ekPC5sugsCFx3xYSw2WPsF" name="Nutribullet Ultra 1200" alt="Nutribullet Ultra 1200 vs budget blender" src="https://cdn.mos.cms.futurecdn.net/ekPC5sugsCFx3xYSw2WPsF.jpg" mos="" align="middle" fullscreen="" width="4096" height="2304" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Whereas I put the contents into a glass for the sake of this article, normally I would just cap off the Nutribullet shaker and be on my way. Similarly, I cranked the Blaupunkt all the way to five on its antiquated knob rather than the simple on/off capacitive touch of the Nutribullet. </p><p>And, last but by no means least, cleaning the Nutribullet was a simpler, quicker affair than my old budget blender.</p><p>So, both got the job done — one just did it with a little more style than the other.</p><h3 class="article-body__section" id="section-final-thoughts"><span>Final thoughts</span></h3><div class="product"><a data-dimension112="e59b75e6-8676-11f1-9acc-718a67d38a99" data-action="Deal Block" data-label="Ultra" data-dimension48="Ultra" data-dimension25="$124" href="https://www.amazon.com/gp/aw/d/B0CBWD3PN7" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1546px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="X2G4ud3wXRFEsYCZi8Hs9C" name="Nutribullet ultra" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/X2G4ud3wXRFEsYCZi8Hs9C.jpg" mos="" align="middle" fullscreen="" width="1546" height="1546" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Nutribullet Ultra 1200 is a powerhouse of a blender boasting a 1200-watt motor, titanium-coated extractor blade and Tritan Renew cups (700ml and 900ml) that are both durable and sustainable. In Nutribullet's signature Gunmetal Grey, it's a sleek yet powerful combo that aims to deliver smoothies in just 30 seconds flat.<a class="view-deal button" href="https://www.amazon.com/gp/aw/d/B0CBWD3PN7" target="_blank" rel="nofollow" data-dimension112="e59b75e6-8676-11f1-9acc-718a67d38a99" data-action="Deal Block" data-label="Ultra" data-dimension48="Ultra" data-dimension25="$124">View Deal</a></p></div><div class="product"><a data-dimension112="e59b7640-8676-11f1-9b53-dd2c078d49e7" data-action="Deal Block" data-label="Ultra" data-dimension48="Ultra" data-dimension25="£129" href="https://www.amazon.co.uk/nutribullet-Ultra-1200W-Personal-Blender/dp/B0CSDWRT8N" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1546px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="X2G4ud3wXRFEsYCZi8Hs9C" name="Nutribullet ultra" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/X2G4ud3wXRFEsYCZi8Hs9C.jpg" mos="" align="middle" fullscreen="" width="1546" height="1546" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/nutribullet-Ultra-1200W-Personal-Blender/dp/B0CSDWRT8N" target="_blank" rel="nofollow" data-dimension112="e59b7640-8676-11f1-9b53-dd2c078d49e7" data-action="Deal Block" data-label="Ultra" data-dimension48="Ultra" data-dimension25="£129">Ultra: £129</a></strong><br>This product is also available in the U.K. where it's currently sitting at full price. During sales events, however, it can drop down to under £100 so it may be worth adding to your wishlist and keeping an eye on it.<a class="view-deal button" href="https://www.amazon.co.uk/nutribullet-Ultra-1200W-Personal-Blender/dp/B0CSDWRT8N" target="_blank" rel="nofollow" data-dimension112="e59b7640-8676-11f1-9b53-dd2c078d49e7" data-action="Deal Block" data-label="Ultra" data-dimension48="Ultra" data-dimension25="£129">View Deal</a></p></div><p>This little experiment has reinforced what I felt about leveling up blenders back at the start of the year. When it comes to the nuts and bolts of the taste of the final product, there's no difference — at least for someone with my unrefined palate.</p><p>Moreover, I can put up with a bit of noise for 30 seconds, so the fact my Nutribullet was louder than advertised is also something I can overlook.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4096px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="WLz7mEum8qLNUquUQUJUrF" name="Nutribullet Ultra 1200" alt="Nutribullet Ultra 1200 vs budget blender" src="https://cdn.mos.cms.futurecdn.net/WLz7mEum8qLNUquUQUJUrF.jpg" mos="" align="middle" fullscreen="" width="4096" height="2304" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>But, all the other aspects of using a blender were far superior with the more expensive model. I like that: <strong>a) it takes up less countertop space, b) it doesn't take as long to clean, c) it comes with a dedicated shaker for less fuss c) it genuinely just looks cooler and d) if it breaks, I can claim on the warranty for longer.</strong></p><p>Are those advantages worth $100 more? For my current lifestyle: yes. If you'd have asked me years ago when I was a student watching every dime I spent, then probably no.</p><p>The lesson I learned here was that despite our constant fixation on the end result, there's more to choosing a product than what it delivers. Less destination, more journey. </p><p>Of course, what works for me might not work for you. I happen to value how easy and quick the Nutribullet is to use so that makes the extra $100 worth it. Do you agree or not? Let me know in the comments below.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XZKP2e"></div>                            </div>                            <script src="https://kwizly.com/embed/XZKP2e.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/news/masticating-vs-centrifugal-juicer-which-is-best">Masticating vs centrifugal juicer: Which is best?</a></li><li><a href="https://www.tomsguide.com/best-picks/best-blenders">This is the best blender in the world, and two trusty, cheaper alternatives</a></li><li><a href="https://www.tomsguide.com/news/juicer-vs-blender-what-is-the-differnce">Juicer vs Blender: What's the difference?</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I swapped my hot oven for an air fryer to bake summer treats — here’s why I'm not going back ]]></title>
                                                                                                <dc:content><![CDATA[ <p>During this scorching heat, what’s a home baker to do? Having the oven switched on to satisfy a craving for sponge cake will have your <a href="https://www.tomsguide.com/us/smart-air-conditioner-buying-guide,review-5615.html">air conditioning</a> on overdrive, or your fan blowing around powdered sugar. But I won’t let the high temperatures stop me from baking, so I put aside my reservations and tried to make some cupcakes in an <a href="https://www.tomsguide.com/best-picks/best-air-fryers">air fryer</a>. </p><p>Could this countertop appliance that I rely on for crisping fries and grilling burgers bake a decent cake? The answer is a resounding yes, but you will have to make some changes.</p><p>Here’s exactly what happened when I baked a batch of lemon cupcakes in my air fryer, along with expert advice on mastering the countertop bake.</p><div class="product"><a data-dimension112="51fde800-81d5-11f1-8bca-ed6595bbd10c" data-action="Deal Block" data-label="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension48="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension25="$139" href="https://www.amazon.com/dp/B0GVPBSXXF?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="t5fXiTswZJ6ouWjQJi7zdi" name="Cuisinart Clear View 4-Quart Air Fryer" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/t5fXiTswZJ6ouWjQJi7zdi.jpg" mos="" align="middle" fullscreen="" width="2000" height="2000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0GVPBSXXF?" target="_blank" rel="nofollow" data-dimension112="51fde800-81d5-11f1-8bca-ed6595bbd10c" data-action="Deal Block" data-label="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension48="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension25="$139">ClearView 4-Quart Air Fryer with Glass Basket: $139</a></strong><br>Cuisinart’s ClearView air fryer has a 4-quart capacity and 5 cooking functions. Choose air fry, bake, broil, keep warm or recrisp. Plus, it has two presets for your favorite settings. Reaching up to 450°F, it has 1600 watts of power and the capacity to cook 1.5 lb of French fries, 2 lbs of wings, or a whole 4 lb chicken. View your food’s progress as it cooks through the clean bowl, which can also works for food prep and storage.<a class="view-deal button" href="https://www.amazon.com/dp/B0GVPBSXXF?" target="_blank" rel="nofollow" data-dimension112="51fde800-81d5-11f1-8bca-ed6595bbd10c" data-action="Deal Block" data-label="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension48="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension25="$139">View Deal</a></p></div><h2 id="how-i-made-lemon-cupcakes-in-an-air-fryer">How I made lemon cupcakes in an air fryer</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4qzU784L8A84aaea4wjWNG" name="Lemon cupcakes made in air fryer" alt="Lemon cupcakes baked in air fryer" src="https://cdn.mos.cms.futurecdn.net/4qzU784L8A84aaea4wjWNG.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>Lemon cupcakes are the ultimate refreshing summer treat and happen to be one of my family’s favorite go-to sweet treats.</p><p>I often search online for recipes, and I found an <a href="https://www.bbcgoodfood.com/recipes/easy-lemon-muffins" target="_blank">Easy lemon muffin recipe</a> at BBC Good Food. What’s brilliant about this particular recipe is that all of the batter ingredients can be tipped into a single bowl and mixed in one go. You can do this by hand or with one of the <a href="https://www.tomsguide.com/best-picks/best-stand-mixers">best stand mixers</a>; either way, as long as your butter is soft, you won’t break a sweat.</p><p>The recipe calls for a muffin tray, which is a non-starter for my compact <a href="https://www.tomsguide.com/home/stop-the-guesswork-cuisinarts-clearview-air-fryer-gives-you-dinner-in-plain-sight">Cuisinart Clearview Air Fryer</a>. To improvise, I doubled up my paper cupcake liners for extra stability. Luckily, they held up well when I spooned in the batter. </p><p>After preheating the air fryer to 200°C  (390°F) for four minutes, I then carefully placed the cupcake cases into the glass basket, but I could only bake five at a time.</p><p>Knowing that air fryers run hotter and faster than conventional ovens, I selected ‘Bake’ mode, at 160°C (320°F) for 15 minutes — 20°C lower and 15 minutes shorter than the original bake time.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZDBV36Tgb2ZmLMg8ukx68H" name="Lemon cupcakes made in air fryer" alt="Lemon cupcakes baked in air fryer" src="https://cdn.mos.cms.futurecdn.net/ZDBV36Tgb2ZmLMg8ukx68H.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I had a massive advantage during this bake thanks to my air fryer’s design. Because of its glass viewing window, I had a full view of the action. Unlike standard air fryer models, I didn’t have to pull the basket to see how the bake was progressing.</p><p>At the 11-minute mark, I noticed the tops had started to brown. By 15 minutes, I was satisfied they were perfectly cooked.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jhFFvU5PiC2kiGgfWbDC4H" name="Lemon cupcakes made in air fryer" alt="Lemon cupcakes baked in air fryer" src="https://cdn.mos.cms.futurecdn.net/jhFFvU5PiC2kiGgfWbDC4H.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><h2 id="what-i-noticed-when-cooking-cupcakes-in-an-air-fryer">What I noticed when cooking cupcakes in an air fryer</h2><p>Although you can cook cupcakes in an air fryer, I did notice a few differences between this method and using a traditional oven.<br><br><strong>1.</strong> You have to reduce the cooking time and temperature.<br><strong>2.</strong> Forget about making a big cake.<br><strong>3. </strong>The final shape is a little different and may need disguising with icing.<br><strong>4.</strong> The bake was just as moist.</p><p>I’d be happy to continue baking in an air fryer, although I won’t be taking on any elaborate challenges. But be warned, even though they didn’t look as appetizing as when baked in a traditional oven, they still tasted delicious.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-exVzoO"></div>                            </div>                            <script src="https://kwizly.com/embed/exVzoO.js" async></script><h3 class="article-body__section" id="section-what-do-the-professionals-think-about-air-fryer-baking"><span>What do the professionals think about air fryer baking?</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="EeDHv7oUxsg27WnTLwUh3H" name="Lemon cupcakes made in air fryer" alt="Lemon cupcakes baked in air fryer" src="https://cdn.mos.cms.futurecdn.net/EeDHv7oUxsg27WnTLwUh3H.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I was able to prove that you can bake a cake in an air fryer, though many factors differ from using an oven. To help you navigate them and ensure a perfect bake every time, I reached out to some baking pros for their insider advice. </p><h2 id="can-baking-a-cake-in-an-air-fryer-deliver-the-same-results-as-an-oven">Can baking a cake in an air fryer deliver the same results as an oven?</h2><p>You can definitely bake a cake in an air fryer, but experts say it’s not a straight swap.</p><p>“The result is not that air fryers bake worse; it is that they bake differently,” explains Andreas Hansen, founder at <a href="https://fritaire.com/" target="_blank">Fritaire</a>. “Dense, moist batters like brownies, banana bread, pound cake, and lava cakes perform genuinely well. Tall, delicate sponges that depend on a slow, even rise are where a conventional oven still wins.”</p><p>Isaac Bernal Carbajo, <a href="https://www.instagram.com/chef_isaac_bernal/" target="_blank">executive chef</a>, explains, “The moving air shoves loose batter around before it can set, which is why delicate sponges and anything chiffon-adjacent are a bad bet. What you do get is speed, no long preheat, and a lovely crust on sturdier bakes.”</p><div><blockquote><p>The result is not that air fryers bake worse; it is that they bake differently</p><p>Andreas Hansen, Fritaire</p></blockquote></div><p>Minoo Mahdavian, founder at <a href="https://minoomakesstuff.com/" target="_blank">MinooMakesStuff</a>, says, “Many people treat an air fryer like a multipurpose ‘little oven’, when it’s actually closer to a wind tunnel with a heating element parked right above your cake.”</p><p><strong>How an air fryer actually works</strong></p><p>It helps to know how an air fryer works to understand why it produces different results from a conventional oven. Hansen explains the three main differences.</p><p><strong>Proximity to heat: </strong>“The heating element sits much closer to the food, which is why tops brown fast.”</p><p><strong>Aggressive airflow:</strong> “The fan moves air more aggressively in a smaller chamber, which can pull moisture from the batter surface before the crumb sets.”</p><p><strong>Rapid heat recovery:</strong> “The small cavity means temperature recovers very quickly when you open it, which is actually an advantage: you lose less heat checking on a cake than you would in a full-size oven.”</p><h2 id="are-there-any-differences-that-would-make-the-bake-better-or-worse">Are there any differences that would make the bake better or worse?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hpP7iXUkvH8BPQYGVCpzSG" name="Lemon cupcakes made in air fryer" alt="Lemon cupcakes baked in air fryer" src="https://cdn.mos.cms.futurecdn.net/hpP7iXUkvH8BPQYGVCpzSG.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>Because air fryers work differently from conventional ovens, if you follow the same baking temperatures for standard recipes, you won’t necessarily get the same result. </p><p>“The fan sets and browns the top long before the middle catches up, so you pull out something that looks gorgeous, cracks hard down the center, and is still gummy in the bottom third,” says Mahdavian.</p><p>Carbajo suggests making a few changes: “I usually lower the temperature slightly compared to the oven recipe, about 15 - 20°F. Start checking on the cake a few minutes early. Every air fryer behaves a little differently, so I suggest practicing once or twice to get the feel of it."</p><h2 id="apart-from-size-are-there-any-other-limitations">Apart from size, are there any other limitations?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1600px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="fJTqCx2aMs3DJffXCJQ3TJ" name="Ninja Pro 4in1 air fryer drawer pulled out" alt="Ninja Pro 4in1 air fryer" src="https://cdn.mos.cms.futurecdn.net/fJTqCx2aMs3DJffXCJQ3TJ.jpg" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide / Millie Fender)</span></figcaption></figure><p>As soon as you start baking in an air fryer, you’ll realize that your conventional baking tins won’t fit. When I made the lemon cupcakes in my air fryer, I had to swap from my habit of making a larger loaf cake and opt for individual paper cases instead.</p><p>Apart from downsizing your bakeware, what else do you consider?<br><br><strong>Pan clearance and airflow</strong><br>“The cake pan needs space around it for air to circulate,” says Hansen. “A pan that fits snugly will bake unevenly because you have effectively blocked the convection that the appliance depends on.”</p><p>Carbajo adds, “Even if a pan technically fits, if it’s crowding the walls or sitting too close to the element, it won’t bake evenly. I use a 6-inch round, anything wider and I’m just making a burnt frisbee.”</p><div><blockquote><p>Even if a pan technically fits, if it's crowding the walls or sitting too close to the element, it won't bake evenly</p><p>Isaac Bernal Carbajo, Executive Chef</p></blockquote></div><p><strong>No bottom heat (in most models)</strong><br>The lack of heat at the bottom of the air fryer can also cause issues with some bakes. Hansen says, “Cakes that need a strong base set, like some cheesecakes and fruit-heavy batters, can end up with soft bottoms.”</p><p><strong>Batter height</strong><br>If you’re hoping to bake a nice tall fruit cake or deep sponge cake, think again. “Anything that rises tall gets close to the top element and browns or burns before the center cooks,” says Hansen, “Low, wide bakes beat tall, narrow ones in an air fryer every time.”<br><br><strong>When using an air fryer makes sense</strong><br>Baking in an air fryer undoubtedly limits the scope of what you can make. However, on hot summer days, when you don’t fancy turning your kitchen into a sauna, an air fryer makes a good swap. </p><p>It’s also an ideal solution if you’re only baking for one or two people and won’t eat your way through a whole cake. </p><h2 id="air-frying-baking-tips">Air frying baking tips</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2zThsCykzzKr2DcXfAmY7E" name="shutterstock_2421039309" alt="Baked muffins in an air fryer" src="https://cdn.mos.cms.futurecdn.net/2zThsCykzzKr2DcXfAmY7E.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>To successfully transition from oven baking to air fryer baking, the experts recommend these five key adjustments.</p><p><strong>1. Lower the temperature</strong><br>Drop the temperature about 35°F (20°C) below the conventional oven recipe. “The compact chamber and close element run effectively hotter,” says Hansen. </p><p><strong>2. Tent with foil</strong><br>As air fryers can dry out a cake, Carbajo suggests covering the top with foil once it starts to brown to prevent moisture from escaping. </p><p><strong>3. Go low and wide</strong><br>Hansen says to go low and wide, and adds, “Two shallow layers bake far better than one tall cake, and you can stack them afterward.”</p><p><strong>4. Don’t overfill</strong><br>Air fryer bakes need more headroom than oven bakes, for this reason Hansen recommends to fill pans halfway to two-thirds, and no more. </p><p><strong>5. Let it rest</strong><br>“Let the cake sit in the residual warmth for a few minutes after the cycle ends,” says Hansen. “The small chamber holds gentle heat that finishes the center without further browning.”</p><p><strong>6. Experiment </strong><br>Be prepared to experiment and find out what works with your air fryer. Mantiri says, “Just like how ovens can behave differently depending on the brand, how old it is, or even the altitude you’re at, a similar thing goes for air fryers.”</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchen-dining/i-discovered-how-to-make-homemade-bread-on-my-air-fryer-heres-how">I discovered how to make homemade bread in my air fryer — here's how</a></li><li><a href="https://www.tomsguide.com/home/ive-never-given-a-product-a-5-star-rating-heres-why-kitchenaids-new-stand-mixer-won-me-over">I’ve never given a product a 5-star rating — here’s why KitchenAid’s new stand mixer won me over</a></li><li><a href="https://www.tomsguide.com/home/kitchen-dining/watch-out-kitchenaid-oonis-launching-its-second-stand-mixer-and-at-usd599-it-price-matches-the-artisan-pro">Watch out KitchenAid! Ooni’s launching its second stand mixer, and at $599 its price matches the Artisan Pro</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/air-fryers/i-tried-baking-in-an-air-fryer-and-now-im-seriously-close-to-ditching-my-oven</link>
                                                                            <description>
                            <![CDATA[ Can an air fryer replace baking a cake in an oven? I tried it to find out, and was pleasantly surprised. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">4SfQAvoxkMF36WtPdBdmAm</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/Ej3xVv4Ak2vhsnS9qdLHYG-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 27 Jul 2026 08:30:00 +0000</pubDate>                                                                                                                                <updated>Thu, 30 Jul 2026 13:58:42 +0000</updated>
                                                                                                                                            <category><![CDATA[Air Fryers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ camilla.sharman@futurenet.com (Camilla Sharman) ]]></author>                    <dc:creator><![CDATA[ Camilla Sharman ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/nY4nvWzofHKHpvzAqN5LVH.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Camilla is the Homes Staff Writer and covers everything to do with homes and gardens. She has a wealth of editorial experience, mounting over 30 years, and covers news and features, tests products for reviews and compiles buying guides.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Her work has appeared in business and consumer titles, including Ideal Home, Real Homes, House Beautiful, Homebuilding &amp;amp; Renovation, and Kitchen &amp;amp; Bathroom Business. She’s even appeared on the cover of Your Home, writing about her own house renovation.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Although she’s obsessed with decorating her home, she also enjoys baking and trying out the latest kitchen appliances. But when she’s not inside, you’ll find her pottering about in her yard, tending to her vegetable patch or taking in her prized hydrangeas. She also enjoys keeping fit, and if she&#039;s not on a spin bike trying to keep up with the class, she&#039;ll be in the pool, or trying to perfect her headstand in a yoga class.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/Ej3xVv4Ak2vhsnS9qdLHYG-1280-80.jpg">
                                                            <media:credit><![CDATA[Future / Camilla Sharman]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Lemon cupcakes baked in air fryer]]></media:description>                                                            <media:text><![CDATA[Lemon cupcakes baked in air fryer]]></media:text>
                                <media:title type="plain"><![CDATA[Lemon cupcakes baked in air fryer]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/Ej3xVv4Ak2vhsnS9qdLHYG-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>During this scorching heat, what’s a home baker to do? Having the oven switched on to satisfy a craving for sponge cake will have your <a href="https://www.tomsguide.com/us/smart-air-conditioner-buying-guide,review-5615.html">air conditioning</a> on overdrive, or your fan blowing around powdered sugar. But I won’t let the high temperatures stop me from baking, so I put aside my reservations and tried to make some cupcakes in an <a href="https://www.tomsguide.com/best-picks/best-air-fryers">air fryer</a>. </p><p>Could this countertop appliance that I rely on for crisping fries and grilling burgers bake a decent cake? The answer is a resounding yes, but you will have to make some changes.</p><p>Here’s exactly what happened when I baked a batch of lemon cupcakes in my air fryer, along with expert advice on mastering the countertop bake.</p><div class="product"><a data-dimension112="51fde800-81d5-11f1-8bca-ed6595bbd10c" data-action="Deal Block" data-label="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension48="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension25="$139" href="https://www.amazon.com/dp/B0GVPBSXXF?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="t5fXiTswZJ6ouWjQJi7zdi" name="Cuisinart Clear View 4-Quart Air Fryer" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/t5fXiTswZJ6ouWjQJi7zdi.jpg" mos="" align="middle" fullscreen="" width="2000" height="2000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0GVPBSXXF?" target="_blank" rel="nofollow" data-dimension112="51fde800-81d5-11f1-8bca-ed6595bbd10c" data-action="Deal Block" data-label="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension48="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension25="$139">ClearView 4-Quart Air Fryer with Glass Basket: $139</a></strong><br>Cuisinart’s ClearView air fryer has a 4-quart capacity and 5 cooking functions. Choose air fry, bake, broil, keep warm or recrisp. Plus, it has two presets for your favorite settings. Reaching up to 450°F, it has 1600 watts of power and the capacity to cook 1.5 lb of French fries, 2 lbs of wings, or a whole 4 lb chicken. View your food’s progress as it cooks through the clean bowl, which can also works for food prep and storage.<a class="view-deal button" href="https://www.amazon.com/dp/B0GVPBSXXF?" target="_blank" rel="nofollow" data-dimension112="51fde800-81d5-11f1-8bca-ed6595bbd10c" data-action="Deal Block" data-label="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension48="ClearView 4-Quart Air Fryer with Glass Basket" data-dimension25="$139">View Deal</a></p></div><h2 id="how-i-made-lemon-cupcakes-in-an-air-fryer">How I made lemon cupcakes in an air fryer</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4qzU784L8A84aaea4wjWNG" name="Lemon cupcakes made in air fryer" alt="Lemon cupcakes baked in air fryer" src="https://cdn.mos.cms.futurecdn.net/4qzU784L8A84aaea4wjWNG.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>Lemon cupcakes are the ultimate refreshing summer treat and happen to be one of my family’s favorite go-to sweet treats.</p><p>I often search online for recipes, and I found an <a href="https://www.bbcgoodfood.com/recipes/easy-lemon-muffins" target="_blank">Easy lemon muffin recipe</a> at BBC Good Food. What’s brilliant about this particular recipe is that all of the batter ingredients can be tipped into a single bowl and mixed in one go. You can do this by hand or with one of the <a href="https://www.tomsguide.com/best-picks/best-stand-mixers">best stand mixers</a>; either way, as long as your butter is soft, you won’t break a sweat.</p><p>The recipe calls for a muffin tray, which is a non-starter for my compact <a href="https://www.tomsguide.com/home/stop-the-guesswork-cuisinarts-clearview-air-fryer-gives-you-dinner-in-plain-sight">Cuisinart Clearview Air Fryer</a>. To improvise, I doubled up my paper cupcake liners for extra stability. Luckily, they held up well when I spooned in the batter. </p><p>After preheating the air fryer to 200°C  (390°F) for four minutes, I then carefully placed the cupcake cases into the glass basket, but I could only bake five at a time.</p><p>Knowing that air fryers run hotter and faster than conventional ovens, I selected ‘Bake’ mode, at 160°C (320°F) for 15 minutes — 20°C lower and 15 minutes shorter than the original bake time.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZDBV36Tgb2ZmLMg8ukx68H" name="Lemon cupcakes made in air fryer" alt="Lemon cupcakes baked in air fryer" src="https://cdn.mos.cms.futurecdn.net/ZDBV36Tgb2ZmLMg8ukx68H.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I had a massive advantage during this bake thanks to my air fryer’s design. Because of its glass viewing window, I had a full view of the action. Unlike standard air fryer models, I didn’t have to pull the basket to see how the bake was progressing.</p><p>At the 11-minute mark, I noticed the tops had started to brown. By 15 minutes, I was satisfied they were perfectly cooked.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jhFFvU5PiC2kiGgfWbDC4H" name="Lemon cupcakes made in air fryer" alt="Lemon cupcakes baked in air fryer" src="https://cdn.mos.cms.futurecdn.net/jhFFvU5PiC2kiGgfWbDC4H.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><h2 id="what-i-noticed-when-cooking-cupcakes-in-an-air-fryer">What I noticed when cooking cupcakes in an air fryer</h2><p>Although you can cook cupcakes in an air fryer, I did notice a few differences between this method and using a traditional oven.<br><br><strong>1.</strong> You have to reduce the cooking time and temperature.<br><strong>2.</strong> Forget about making a big cake.<br><strong>3. </strong>The final shape is a little different and may need disguising with icing.<br><strong>4.</strong> The bake was just as moist.</p><p>I’d be happy to continue baking in an air fryer, although I won’t be taking on any elaborate challenges. But be warned, even though they didn’t look as appetizing as when baked in a traditional oven, they still tasted delicious.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-exVzoO"></div>                            </div>                            <script src="https://kwizly.com/embed/exVzoO.js" async></script><h3 class="article-body__section" id="section-what-do-the-professionals-think-about-air-fryer-baking"><span>What do the professionals think about air fryer baking?</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="EeDHv7oUxsg27WnTLwUh3H" name="Lemon cupcakes made in air fryer" alt="Lemon cupcakes baked in air fryer" src="https://cdn.mos.cms.futurecdn.net/EeDHv7oUxsg27WnTLwUh3H.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I was able to prove that you can bake a cake in an air fryer, though many factors differ from using an oven. To help you navigate them and ensure a perfect bake every time, I reached out to some baking pros for their insider advice. </p><h2 id="can-baking-a-cake-in-an-air-fryer-deliver-the-same-results-as-an-oven">Can baking a cake in an air fryer deliver the same results as an oven?</h2><p>You can definitely bake a cake in an air fryer, but experts say it’s not a straight swap.</p><p>“The result is not that air fryers bake worse; it is that they bake differently,” explains Andreas Hansen, founder at <a href="https://fritaire.com/" target="_blank">Fritaire</a>. “Dense, moist batters like brownies, banana bread, pound cake, and lava cakes perform genuinely well. Tall, delicate sponges that depend on a slow, even rise are where a conventional oven still wins.”</p><p>Isaac Bernal Carbajo, <a href="https://www.instagram.com/chef_isaac_bernal/" target="_blank">executive chef</a>, explains, “The moving air shoves loose batter around before it can set, which is why delicate sponges and anything chiffon-adjacent are a bad bet. What you do get is speed, no long preheat, and a lovely crust on sturdier bakes.”</p><div><blockquote><p>The result is not that air fryers bake worse; it is that they bake differently</p><p>Andreas Hansen, Fritaire</p></blockquote></div><p>Minoo Mahdavian, founder at <a href="https://minoomakesstuff.com/" target="_blank">MinooMakesStuff</a>, says, “Many people treat an air fryer like a multipurpose ‘little oven’, when it’s actually closer to a wind tunnel with a heating element parked right above your cake.”</p><p><strong>How an air fryer actually works</strong></p><p>It helps to know how an air fryer works to understand why it produces different results from a conventional oven. Hansen explains the three main differences.</p><p><strong>Proximity to heat: </strong>“The heating element sits much closer to the food, which is why tops brown fast.”</p><p><strong>Aggressive airflow:</strong> “The fan moves air more aggressively in a smaller chamber, which can pull moisture from the batter surface before the crumb sets.”</p><p><strong>Rapid heat recovery:</strong> “The small cavity means temperature recovers very quickly when you open it, which is actually an advantage: you lose less heat checking on a cake than you would in a full-size oven.”</p><h2 id="are-there-any-differences-that-would-make-the-bake-better-or-worse">Are there any differences that would make the bake better or worse?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hpP7iXUkvH8BPQYGVCpzSG" name="Lemon cupcakes made in air fryer" alt="Lemon cupcakes baked in air fryer" src="https://cdn.mos.cms.futurecdn.net/hpP7iXUkvH8BPQYGVCpzSG.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>Because air fryers work differently from conventional ovens, if you follow the same baking temperatures for standard recipes, you won’t necessarily get the same result. </p><p>“The fan sets and browns the top long before the middle catches up, so you pull out something that looks gorgeous, cracks hard down the center, and is still gummy in the bottom third,” says Mahdavian.</p><p>Carbajo suggests making a few changes: “I usually lower the temperature slightly compared to the oven recipe, about 15 - 20°F. Start checking on the cake a few minutes early. Every air fryer behaves a little differently, so I suggest practicing once or twice to get the feel of it."</p><h2 id="apart-from-size-are-there-any-other-limitations">Apart from size, are there any other limitations?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1600px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="fJTqCx2aMs3DJffXCJQ3TJ" name="Ninja Pro 4in1 air fryer drawer pulled out" alt="Ninja Pro 4in1 air fryer" src="https://cdn.mos.cms.futurecdn.net/fJTqCx2aMs3DJffXCJQ3TJ.jpg" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide / Millie Fender)</span></figcaption></figure><p>As soon as you start baking in an air fryer, you’ll realize that your conventional baking tins won’t fit. When I made the lemon cupcakes in my air fryer, I had to swap from my habit of making a larger loaf cake and opt for individual paper cases instead.</p><p>Apart from downsizing your bakeware, what else do you consider?<br><br><strong>Pan clearance and airflow</strong><br>“The cake pan needs space around it for air to circulate,” says Hansen. “A pan that fits snugly will bake unevenly because you have effectively blocked the convection that the appliance depends on.”</p><p>Carbajo adds, “Even if a pan technically fits, if it’s crowding the walls or sitting too close to the element, it won’t bake evenly. I use a 6-inch round, anything wider and I’m just making a burnt frisbee.”</p><div><blockquote><p>Even if a pan technically fits, if it's crowding the walls or sitting too close to the element, it won't bake evenly</p><p>Isaac Bernal Carbajo, Executive Chef</p></blockquote></div><p><strong>No bottom heat (in most models)</strong><br>The lack of heat at the bottom of the air fryer can also cause issues with some bakes. Hansen says, “Cakes that need a strong base set, like some cheesecakes and fruit-heavy batters, can end up with soft bottoms.”</p><p><strong>Batter height</strong><br>If you’re hoping to bake a nice tall fruit cake or deep sponge cake, think again. “Anything that rises tall gets close to the top element and browns or burns before the center cooks,” says Hansen, “Low, wide bakes beat tall, narrow ones in an air fryer every time.”<br><br><strong>When using an air fryer makes sense</strong><br>Baking in an air fryer undoubtedly limits the scope of what you can make. However, on hot summer days, when you don’t fancy turning your kitchen into a sauna, an air fryer makes a good swap. </p><p>It’s also an ideal solution if you’re only baking for one or two people and won’t eat your way through a whole cake. </p><h2 id="air-frying-baking-tips">Air frying baking tips</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2zThsCykzzKr2DcXfAmY7E" name="shutterstock_2421039309" alt="Baked muffins in an air fryer" src="https://cdn.mos.cms.futurecdn.net/2zThsCykzzKr2DcXfAmY7E.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>To successfully transition from oven baking to air fryer baking, the experts recommend these five key adjustments.</p><p><strong>1. Lower the temperature</strong><br>Drop the temperature about 35°F (20°C) below the conventional oven recipe. “The compact chamber and close element run effectively hotter,” says Hansen. </p><p><strong>2. Tent with foil</strong><br>As air fryers can dry out a cake, Carbajo suggests covering the top with foil once it starts to brown to prevent moisture from escaping. </p><p><strong>3. Go low and wide</strong><br>Hansen says to go low and wide, and adds, “Two shallow layers bake far better than one tall cake, and you can stack them afterward.”</p><p><strong>4. Don’t overfill</strong><br>Air fryer bakes need more headroom than oven bakes, for this reason Hansen recommends to fill pans halfway to two-thirds, and no more. </p><p><strong>5. Let it rest</strong><br>“Let the cake sit in the residual warmth for a few minutes after the cycle ends,” says Hansen. “The small chamber holds gentle heat that finishes the center without further browning.”</p><p><strong>6. Experiment </strong><br>Be prepared to experiment and find out what works with your air fryer. Mantiri says, “Just like how ovens can behave differently depending on the brand, how old it is, or even the altitude you’re at, a similar thing goes for air fryers.”</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchen-dining/i-discovered-how-to-make-homemade-bread-on-my-air-fryer-heres-how">I discovered how to make homemade bread in my air fryer — here's how</a></li><li><a href="https://www.tomsguide.com/home/ive-never-given-a-product-a-5-star-rating-heres-why-kitchenaids-new-stand-mixer-won-me-over">I’ve never given a product a 5-star rating — here’s why KitchenAid’s new stand mixer won me over</a></li><li><a href="https://www.tomsguide.com/home/kitchen-dining/watch-out-kitchenaid-oonis-launching-its-second-stand-mixer-and-at-usd599-it-price-matches-the-artisan-pro">Watch out KitchenAid! Ooni’s launching its second stand mixer, and at $599 its price matches the Artisan Pro</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ You told me your three biggest issues with Moccamaster — and I have solutions for all of them ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the seventeenth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>I love my <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster</a>. It's one of the <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers">best coffee makers</a> in the world, by a mile. It brews consistently delicious coffee with zero bitterness, minimal silt, and looks darn cool while doing it. </p><p>However, not everyone gets on with Moccamaster as well as I do — and that's the nature of life. Some issues are integral, which means those people would likely be better off with an alternative. But some are super easy fixes — and two of these three issues are <em>incredibly</em> easy fixes. </p><p>Last week, I troubleshot three other Moccamaster problems: "too much plastic," "horrible water distribution," and the "hot plate burning the coffee." This week, I'm teaching you how to prevent the Moccamaster from "scaling easily," dealing with the Moccamaster's "high price," and my favorite question of all: Does the Moccamaster come in a single-serve model?</p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7666132122834341134" data-video-id="7666132122834341134" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ Jazz, ambient taste, hip-ho ... - TOKYO Lonesome Blue" href="https://www.tiktok.com/music/Jazz-ambient-taste-hip-ho-6817677146459211778">♬ Jazz, ambient taste, hip-ho ... - TOKYO Lonesome Blue</a></section>                    </blockquote></div>                <div class="product"><a data-dimension112="1acfeb9e-8694-11f1-be07-451ef8a8d123" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$239" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2297px;"><p class="vanilla-image-block" style="padding-top:108.84%;"><img id="ViCKNPMdH4jYKkGFaBFznF" name="moccamaster_sorbet_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ViCKNPMdH4jYKkGFaBFznF.jpg" mos="" align="middle" fullscreen="" width="2297" height="2500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="1acfeb9e-8694-11f1-be07-451ef8a8d123" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$239">Moccamaster KGBV Select: was $369 now $239</a></strong><br>There's a reason why the <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster</a> is so revered in the coffee world: it seriously is that good. It's super easy to use, SCA-accredited (for both full-batch and half-batch), and makes full-bodied-but-not-bitter coffee.  </p><p><strong>Warranty check:</strong> 5 years as standard and <em>lifetime repairs</em>.<a class="view-deal button" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="1acfeb9e-8694-11f1-be07-451ef8a8d123" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$239">View Deal</a></p></div><h2 id="you-said-the-moccamaster-scales-easily">You said the Moccamaster "scales easily"</h2><p>I <em>loved</em> receiving this question — because it's so easily fixed! The Moccamaster will scale up. Everything that touches water will scale up. Pretty much all water contains minerals, which create limescale. </p><p>However, this is super easy to deal with.</p><div class="product"><a data-dimension112="14dbad1e-8693-11f1-b8af-21193dcaa8f1" data-action="Deal Block" data-label="Descaler" data-dimension48="Descaler" data-dimension25="$16" href="https://www.amazon.com/Durgol-0298-Universal-Multipurpose-Decalcifier/dp/B076D99C1B" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="cW8kTnKEQiWWwMVxM5zGUE" name="durgol descaler" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/cW8kTnKEQiWWwMVxM5zGUE.png" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This is the descaler Moccamaster recommends. All you have to do is dilute the solution in water, run a brew cycle, and then flush it out with clean water. It's so straightforward.<a class="view-deal button" href="https://www.amazon.com/Durgol-0298-Universal-Multipurpose-Decalcifier/dp/B076D99C1B" target="_blank" rel="nofollow" data-dimension112="14dbad1e-8693-11f1-b8af-21193dcaa8f1" data-action="Deal Block" data-label="Descaler" data-dimension48="Descaler" data-dimension25="$16">View Deal</a></p></div><p>And there you have it! Super easy. Didn't I tell you?</p><h2 id="you-said-the-moccamaster-is-too-expensive">You said the Moccamaster is "too expensive"</h2><p>I wasn't surprised when I received this question. The Moccamaster <em>is</em> expensive. Personally, I think the <a href="https://www.tomsguide.com/home/coffee-makers/the-moccamaster-is-the-perfect-example-of-buy-it-nice-or-buy-it-twice-i-did-the-coffee-math-vs-bonavita-breville-and-more-so-you-dont-have-to">Moccamaster is the perfect example of "buy it nice or buy it twice"</a>. I would rather spend $370 on the Moccamaster than buy a $100 coffee maker three times — but I know that's not realistic for some people.</p><p>Moccamaster also offers a 5-year warranty and lifetime repairs, which no other coffee machine brand has matched yet. Again, personally, I would invest in the Moccamaster due to this after-purchase benefit — but I know that's not realistic for some people. </p><p>Here are some coffee makers with a lower initial cost. </p><div class="product"><a data-dimension112="fef08884-8693-11f1-a8e4-012ef16156b4" data-action="Deal Block" data-label="12-Cup Coffee Maker" data-dimension48="12-Cup Coffee Maker" data-dimension25="$89" href="https://www.amazon.com/Ninja-12-Cup-Programmable-CE251-Stainless/dp/B07S98411N" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="bmf4eyPRXLCcbTSHSFkUhE" name="Ninja 12 Cup Coffee maker.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/bmf4eyPRXLCcbTSHSFkUhE.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-12-Cup-Programmable-CE251-Stainless/dp/B07S98411N" target="_blank" rel="nofollow" data-dimension112="fef08884-8693-11f1-a8e4-012ef16156b4" data-action="Deal Block" data-label="12-Cup Coffee Maker" data-dimension48="12-Cup Coffee Maker" data-dimension25="$89">12-Cup Coffee Maker: $89</a></strong><br>I always recommend this model as a super-cheap Moccamaster alternative. While it's not available in the U.K., therefore I've not been able to personally test it, my U.S.-based colleagues have it in the New York office and <em>love it</em>. This model can make coffee in two styles: "Classic" and "Rich" and has a delayed brew start so you can wake up to fresh coffee every morning. </p><p><strong>Warranty check:</strong> 1 year standard.<a class="view-deal button" href="https://www.amazon.com/Ninja-12-Cup-Programmable-CE251-Stainless/dp/B07S98411N" target="_blank" rel="nofollow" data-dimension112="fef08884-8693-11f1-a8e4-012ef16156b4" data-action="Deal Block" data-label="12-Cup Coffee Maker" data-dimension48="12-Cup Coffee Maker" data-dimension25="$89">View Deal</a></p></div><div class="product"><a data-dimension112="fef088fc-8693-11f1-ae11-93d654b4b3ea" data-action="Deal Block" data-label="5-Cup Coffee Maker" data-dimension48="5-Cup Coffee Maker" data-dimension25="$119" href="https://www.amazon.com/dp/B0DK5B7L6V" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1664px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="4J8VE2KSc7uX8rkWUn5bJH" name="bonavita 5-cup coffee maker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4J8VE2KSc7uX8rkWUn5bJH.jpg" mos="" align="middle" fullscreen="" width="1664" height="1664" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Bonavita makes some majorly affordable coffee makers, and the 5-Cup Coffee Maker is definitely one of them. You'd think a coffee maker this cheap wouldn't be SCA-accredited, but it actually is! As far as I'm aware, this is the cheapest SCA-accredited drip brewer out there. </p><p><strong>Warranty check: </strong>1 year limited.<a class="view-deal button" href="https://www.amazon.com/dp/B0DK5B7L6V" target="_blank" rel="nofollow" data-dimension112="fef088fc-8693-11f1-ae11-93d654b4b3ea" data-action="Deal Block" data-label="5-Cup Coffee Maker" data-dimension48="5-Cup Coffee Maker" data-dimension25="$119">View Deal</a></p></div><p>But Moccamaster also make a cheaper alternative...</p><h2 id="you-said-the-moccamaster-should-make-a-single-pot-version">You said the Moccamaster should make a "single-pot version"</h2><p>This was my favorite question to receive because I have a definitive answer for this: Moccamaster makes the Cup-One, too! I <a href="https://www.tomsguide.com/home/home-appliances/moccamaster-cup-one-review">reviewed the Cup-One</a> last year and loved it. It's exactly the same as the standard Moccamaster KGBV Select — but smaller.</p><div class="product"><a data-dimension112="b74dc9c4-868e-11f1-bee8-ad64bf0f7212" data-action="Deal Block" data-label="Moccamaster Cup-One" data-dimension48="Moccamaster Cup-One" data-dimension25="$250" href="https://www.amazon.com/Technivorm-Moccamaster-69212-Polished-Silver/dp/B014I5OWCO" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="noLEzUB33zqhKEgg2ojgS6" name="moccamaster cup-one deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/noLEzUB33zqhKEgg2ojgS6.jpg" mos="" align="middle" fullscreen="" width="2000" height="2000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Moccamaster Cup-One is the perfect drip brewer for solo households or small households — or if you want fresh coffee every time. You can brew one cup at a time in just a few minutes.<a class="view-deal button" href="https://www.amazon.com/Technivorm-Moccamaster-69212-Polished-Silver/dp/B014I5OWCO" target="_blank" rel="nofollow" data-dimension112="b74dc9c4-868e-11f1-bee8-ad64bf0f7212" data-action="Deal Block" data-label="Moccamaster Cup-One" data-dimension48="Moccamaster Cup-One" data-dimension25="$250">View Deal</a></p></div><p>I recommend the Cup-One to anyone who wants the consistent, stress-free performance of the Moccamaster without being obligated to brew an entire carafe. </p><p>So that brings us to the end! I've answered all the questions you asked me. Any more? Leave them in the comments and I'll get back to you shortly. </p><p>Stay tuned for the next episode of The Coffee Lab. I release a video every Friday on our Instagram, TikTok, and YouTube. </p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-the-new-nespresso-summer-flavors-heres-my-favorite"><strong>I tried the new Nespresso summer flavors — here's my favorite</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/non-plastic-coffee-makers-are-trending-and-this-low-plastic-ratio-model-is-my-favorite-for-coffee-snobs"><strong>Non-plastic coffee makers are trending — and this low-plastic Ratio model is my favorite for coffee snobs</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/you-told-me-your-three-biggest-issues-with-moccamaster-and-i-have-solutions-for-all-of-them</link>
                                                                            <description>
                            <![CDATA[ On the sixteenth episode of The Coffee Lab, I answer your most-asked questions about the Moccamaster: Part 2! ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">uPUMRSWAY5cYnLKQFBBwj</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/5WxCooa2RG8Cwr85JEe9pP-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 24 Jul 2026 13:00:00 +0000</pubDate>                                                                                                                                <updated>Wed, 29 Jul 2026 09:26:51 +0000</updated>
                                                                                                                                            <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/5WxCooa2RG8Cwr85JEe9pP-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[a side-by-side image of the moccamaster and the moccamaster cup one with the coffee lab logo]]></media:description>                                                            <media:text><![CDATA[a side-by-side image of the moccamaster and the moccamaster cup one with the coffee lab logo]]></media:text>
                                <media:title type="plain"><![CDATA[a side-by-side image of the moccamaster and the moccamaster cup one with the coffee lab logo]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/5WxCooa2RG8Cwr85JEe9pP-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the seventeenth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>I love my <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster</a>. It's one of the <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers">best coffee makers</a> in the world, by a mile. It brews consistently delicious coffee with zero bitterness, minimal silt, and looks darn cool while doing it. </p><p>However, not everyone gets on with Moccamaster as well as I do — and that's the nature of life. Some issues are integral, which means those people would likely be better off with an alternative. But some are super easy fixes — and two of these three issues are <em>incredibly</em> easy fixes. </p><p>Last week, I troubleshot three other Moccamaster problems: "too much plastic," "horrible water distribution," and the "hot plate burning the coffee." This week, I'm teaching you how to prevent the Moccamaster from "scaling easily," dealing with the Moccamaster's "high price," and my favorite question of all: Does the Moccamaster come in a single-serve model?</p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7666132122834341134" data-video-id="7666132122834341134" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ Jazz, ambient taste, hip-ho ... - TOKYO Lonesome Blue" href="https://www.tiktok.com/music/Jazz-ambient-taste-hip-ho-6817677146459211778">♬ Jazz, ambient taste, hip-ho ... - TOKYO Lonesome Blue</a></section>                    </blockquote></div>                <div class="product"><a data-dimension112="1acfeb9e-8694-11f1-be07-451ef8a8d123" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$239" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2297px;"><p class="vanilla-image-block" style="padding-top:108.84%;"><img id="ViCKNPMdH4jYKkGFaBFznF" name="moccamaster_sorbet_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ViCKNPMdH4jYKkGFaBFznF.jpg" mos="" align="middle" fullscreen="" width="2297" height="2500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="1acfeb9e-8694-11f1-be07-451ef8a8d123" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$239">Moccamaster KGBV Select: was $369 now $239</a></strong><br>There's a reason why the <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster</a> is so revered in the coffee world: it seriously is that good. It's super easy to use, SCA-accredited (for both full-batch and half-batch), and makes full-bodied-but-not-bitter coffee.  </p><p><strong>Warranty check:</strong> 5 years as standard and <em>lifetime repairs</em>.<a class="view-deal button" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="1acfeb9e-8694-11f1-be07-451ef8a8d123" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$239">View Deal</a></p></div><h2 id="you-said-the-moccamaster-scales-easily">You said the Moccamaster "scales easily"</h2><p>I <em>loved</em> receiving this question — because it's so easily fixed! The Moccamaster will scale up. Everything that touches water will scale up. Pretty much all water contains minerals, which create limescale. </p><p>However, this is super easy to deal with.</p><div class="product"><a data-dimension112="14dbad1e-8693-11f1-b8af-21193dcaa8f1" data-action="Deal Block" data-label="Descaler" data-dimension48="Descaler" data-dimension25="$16" href="https://www.amazon.com/Durgol-0298-Universal-Multipurpose-Decalcifier/dp/B076D99C1B" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="cW8kTnKEQiWWwMVxM5zGUE" name="durgol descaler" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/cW8kTnKEQiWWwMVxM5zGUE.png" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This is the descaler Moccamaster recommends. All you have to do is dilute the solution in water, run a brew cycle, and then flush it out with clean water. It's so straightforward.<a class="view-deal button" href="https://www.amazon.com/Durgol-0298-Universal-Multipurpose-Decalcifier/dp/B076D99C1B" target="_blank" rel="nofollow" data-dimension112="14dbad1e-8693-11f1-b8af-21193dcaa8f1" data-action="Deal Block" data-label="Descaler" data-dimension48="Descaler" data-dimension25="$16">View Deal</a></p></div><p>And there you have it! Super easy. Didn't I tell you?</p><h2 id="you-said-the-moccamaster-is-too-expensive">You said the Moccamaster is "too expensive"</h2><p>I wasn't surprised when I received this question. The Moccamaster <em>is</em> expensive. Personally, I think the <a href="https://www.tomsguide.com/home/coffee-makers/the-moccamaster-is-the-perfect-example-of-buy-it-nice-or-buy-it-twice-i-did-the-coffee-math-vs-bonavita-breville-and-more-so-you-dont-have-to">Moccamaster is the perfect example of "buy it nice or buy it twice"</a>. I would rather spend $370 on the Moccamaster than buy a $100 coffee maker three times — but I know that's not realistic for some people.</p><p>Moccamaster also offers a 5-year warranty and lifetime repairs, which no other coffee machine brand has matched yet. Again, personally, I would invest in the Moccamaster due to this after-purchase benefit — but I know that's not realistic for some people. </p><p>Here are some coffee makers with a lower initial cost. </p><div class="product"><a data-dimension112="fef08884-8693-11f1-a8e4-012ef16156b4" data-action="Deal Block" data-label="12-Cup Coffee Maker" data-dimension48="12-Cup Coffee Maker" data-dimension25="$89" href="https://www.amazon.com/Ninja-12-Cup-Programmable-CE251-Stainless/dp/B07S98411N" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="bmf4eyPRXLCcbTSHSFkUhE" name="Ninja 12 Cup Coffee maker.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/bmf4eyPRXLCcbTSHSFkUhE.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-12-Cup-Programmable-CE251-Stainless/dp/B07S98411N" target="_blank" rel="nofollow" data-dimension112="fef08884-8693-11f1-a8e4-012ef16156b4" data-action="Deal Block" data-label="12-Cup Coffee Maker" data-dimension48="12-Cup Coffee Maker" data-dimension25="$89">12-Cup Coffee Maker: $89</a></strong><br>I always recommend this model as a super-cheap Moccamaster alternative. While it's not available in the U.K., therefore I've not been able to personally test it, my U.S.-based colleagues have it in the New York office and <em>love it</em>. This model can make coffee in two styles: "Classic" and "Rich" and has a delayed brew start so you can wake up to fresh coffee every morning. </p><p><strong>Warranty check:</strong> 1 year standard.<a class="view-deal button" href="https://www.amazon.com/Ninja-12-Cup-Programmable-CE251-Stainless/dp/B07S98411N" target="_blank" rel="nofollow" data-dimension112="fef08884-8693-11f1-a8e4-012ef16156b4" data-action="Deal Block" data-label="12-Cup Coffee Maker" data-dimension48="12-Cup Coffee Maker" data-dimension25="$89">View Deal</a></p></div><div class="product"><a data-dimension112="fef088fc-8693-11f1-ae11-93d654b4b3ea" data-action="Deal Block" data-label="5-Cup Coffee Maker" data-dimension48="5-Cup Coffee Maker" data-dimension25="$119" href="https://www.amazon.com/dp/B0DK5B7L6V" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1664px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="4J8VE2KSc7uX8rkWUn5bJH" name="bonavita 5-cup coffee maker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4J8VE2KSc7uX8rkWUn5bJH.jpg" mos="" align="middle" fullscreen="" width="1664" height="1664" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Bonavita makes some majorly affordable coffee makers, and the 5-Cup Coffee Maker is definitely one of them. You'd think a coffee maker this cheap wouldn't be SCA-accredited, but it actually is! As far as I'm aware, this is the cheapest SCA-accredited drip brewer out there. </p><p><strong>Warranty check: </strong>1 year limited.<a class="view-deal button" href="https://www.amazon.com/dp/B0DK5B7L6V" target="_blank" rel="nofollow" data-dimension112="fef088fc-8693-11f1-ae11-93d654b4b3ea" data-action="Deal Block" data-label="5-Cup Coffee Maker" data-dimension48="5-Cup Coffee Maker" data-dimension25="$119">View Deal</a></p></div><p>But Moccamaster also make a cheaper alternative...</p><h2 id="you-said-the-moccamaster-should-make-a-single-pot-version">You said the Moccamaster should make a "single-pot version"</h2><p>This was my favorite question to receive because I have a definitive answer for this: Moccamaster makes the Cup-One, too! I <a href="https://www.tomsguide.com/home/home-appliances/moccamaster-cup-one-review">reviewed the Cup-One</a> last year and loved it. It's exactly the same as the standard Moccamaster KGBV Select — but smaller.</p><div class="product"><a data-dimension112="b74dc9c4-868e-11f1-bee8-ad64bf0f7212" data-action="Deal Block" data-label="Moccamaster Cup-One" data-dimension48="Moccamaster Cup-One" data-dimension25="$250" href="https://www.amazon.com/Technivorm-Moccamaster-69212-Polished-Silver/dp/B014I5OWCO" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="noLEzUB33zqhKEgg2ojgS6" name="moccamaster cup-one deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/noLEzUB33zqhKEgg2ojgS6.jpg" mos="" align="middle" fullscreen="" width="2000" height="2000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Moccamaster Cup-One is the perfect drip brewer for solo households or small households — or if you want fresh coffee every time. You can brew one cup at a time in just a few minutes.<a class="view-deal button" href="https://www.amazon.com/Technivorm-Moccamaster-69212-Polished-Silver/dp/B014I5OWCO" target="_blank" rel="nofollow" data-dimension112="b74dc9c4-868e-11f1-bee8-ad64bf0f7212" data-action="Deal Block" data-label="Moccamaster Cup-One" data-dimension48="Moccamaster Cup-One" data-dimension25="$250">View Deal</a></p></div><p>I recommend the Cup-One to anyone who wants the consistent, stress-free performance of the Moccamaster without being obligated to brew an entire carafe. </p><p>So that brings us to the end! I've answered all the questions you asked me. Any more? Leave them in the comments and I'll get back to you shortly. </p><p>Stay tuned for the next episode of The Coffee Lab. I release a video every Friday on our Instagram, TikTok, and YouTube. </p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-the-new-nespresso-summer-flavors-heres-my-favorite"><strong>I tried the new Nespresso summer flavors — here's my favorite</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/non-plastic-coffee-makers-are-trending-and-this-low-plastic-ratio-model-is-my-favorite-for-coffee-snobs"><strong>Non-plastic coffee makers are trending — and this low-plastic Ratio model is my favorite for coffee snobs</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested the Ninja Luxe Cafe Mini Plus — and it’s ideal for total beginners due to this one game-changing feature ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Ninja Luxe Cafe Mini Plus does the one thing I know most new home baristas struggle with: dialing in. Ever wondered why nothing is coming out of your espresso machine, or it’s coming out like water from a newly unblocked pipe? It’s because you haven’t dialed your coffee in yet. </p><p>But Ninja said ‘No more!’ No longer do you have to waste precious coffee on figuring out optimal grind size’ The Luxe Cafe Mini Plus has auto-grind-size calibration that tells you the precise “click” you should grind your beans at. This is genuinely <em>so</em> helpful for newbies.</p><p>So, is this enough to make the Ninja Luxe Cafe Mini Plus one of the <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">best espresso machines</a>? For beginners, totally. Say goodbye to coffee-related headaches (unless you’re slamming double espresso after double espresso, but that’s a completely different type of headache). Want to find out more? Why wouldn’t you? Keep reading this Ninja Luxe Cafe Mini Plus review. </p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-specs"><span>Ninja Luxe Cafe Mini Plus review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Ninja-Precision-Cappuccinos-Stainless-ES301/dp/B0GVTV3QTR" target="_blank" rel="nofollow">$499</a> / <a href="https://www.amazon.co.uk/Ninja-Espresso-Machine-Automatic-ES351UK/dp/B0GXB9XN2P" target="_blank" rel="nofollow">£499 </a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>19 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>10.2 x 12.2 x 13.4 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Grinder</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Heating system </strong></p></td><td  ><p>Not known</p></td></tr><tr><td class="firstcol " ><p><strong>Pressure </strong></p></td><td  ><p>Not known</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank capacity</strong></p></td><td  ><p>44 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>Milk jug</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-price-availability"><span>Ninja Luxe Cafe Mini Plus review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4LkvELxSJJcegS2zUSmzxG" name="Ninja Luxe Cafe Mini Plus 8.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/4LkvELxSJJcegS2zUSmzxG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Ninja Luxe Cafe Mini Plus is <a href="https://www.sharkninja.com/ninja-luxe-cafe-mini-plus-autofroth-espresso-machine/ES351.html?dwvar_ES351_color=dbdbdb" target="_blank" rel="nofollow">$499 from SharkNinja U.S.</a> and <a href="https://www.amazon.co.uk/Ninja-Espresso-Machine-Automatic-ES351UK/dp/B0GXB9XN2P" target="_blank" rel="nofollow">£499 from Amazon U.K.</a>. Although this looks like an objectively large amount of money, I can assure you this is actually reasonable for an espresso machine with a built-in grinder. </p><p>Ninja also makes the <a href="https://www.amazon.com/Ninja-Precision-Cappuccinos-Stainless-ES301/dp/B0GVTV3QTR" target="_blank" rel="nofollow">Luxe Cafe Mini </a>($459). The Mini doesn’t have automatic milk frothing. On the Luxe Cafe lineup is the <a href="https://www.tomsguide.com/home/coffee-makers/ninja-luxe-cafe-pro-espresso-machine-review">Luxe Cafe Pro</a> ($749), Luxe Cafe Premier ($599), and then you’ve got the eye-watering <a href="https://www.tomsguide.com/home/coffee-makers/ninja-auto-barista-pro-review">AutoBarista Pro</a> ($949). </p><p>The cheapest setup I’d recommend is the <a href="https://www.tomsguide.com/home/coffee-makers/casabrews-3700-essential-review">Casabrews 3700 Essential </a>($129) and the <a href="https://www.tomsguide.com/home/kitchen-dining/timemore-chestnut-c2s-review">Timemore C2s Chestnut</a> ($60), which works out to $189. However, this is a hand grinder, so it might not be best for those who have no desire to spend ten minutes turning a crank (me included — I make my boyfriend grind my beans). </p><p>When you get into bean-to-cups, the price increases dramatically. You’ve got the Casabrews Marenza ($349), the cheapest bean-to-cup I’ve ever reviewed, but there’s a catch — the grinder hopper smashed after the second use, so I wouldn’t recommend it for longevity. </p><p>You’ve also got the <a href="https://www.tomsguide.com/reviews/breville-barista-express-espresso-machine">Breville Barista Express</a> ($699), one of the most iconic bean-to-cups ever made, and the <a href="https://www.tomsguide.com/home/coffee-makers/delonghi-la-specialista-arte-evo-review">De’Longhi La Specialista Arte Evo</a> ($699), the best-value bean-to-cup I’ve reviewed. The Luxe Cafe Mini Plus undercuts both by some margin.</p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-design"><span>Ninja Luxe Cafe Mini Plus review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CvCpiLCfATsdnrnKe6B2jG" name="Ninja Luxe Cafe Mini Plus 3.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/CvCpiLCfATsdnrnKe6B2jG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Just like the Ninja AutoBarista Pro I reviewed last month, the Luxe Cafe Mini is effectively a big plastic square. The machine gets the job done, but I don’t think it’s as pretty as the Breville Barista Express, De’Longhi Arte Evo, <a href="https://www.tomsguide.com/home/coffee-makers/lelit-mara-x3-review">Lelit Mara X3</a>, or the <a href="https://www.tomsguide.com/home/coffee-makers/fellow-espresso-series-1-review">Fellow Espresso Series 1</a>. </p><p>Of course, though, beauty is in the eye of the beholder, and you may totally disagree. That’s fine! Ninja knows what works, obviously; it wouldn’t keep releasing espresso machines with the same functional aesthetic otherwise. It must work.</p><p>What separates the Ninja Luxe Cafe Mini Plus from the Luxe Cafe Pro or AutoBarista Pro is pretty obvious — its size. The Luxe Cafe Mini is 3 inches narrower, half an inch slimmer, and 1 inch lower than the Luxe Cafe Pro. I’d recommend it for small spaces any day. </p><p>There’s an LCD screen on the front of the machine: you can see your grind size and coffee settings here. It’s easy to change said settings: just twist the knob and press the buttons. They’re all labeled, and it’s practically impossible to go wrong here. </p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-espresso"><span>Ninja Luxe Cafe Mini Plus review: Espresso</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AQEozpYzkWHx2mu9KKQivG" name="Ninja Luxe Cafe Mini Plus 2.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/AQEozpYzkWHx2mu9KKQivG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As this is a bean-to-cup, I’ll discuss grinder and espresso here. I’ll go through milk performance later. The grinder operates at 71 dB, which is around the same volume as a running shower or a flushing toilet. As it only grinds for a few seconds, I’d say this is pretty quiet, all things considered. </p><h2 id="grinder-2">Grinder</h2><p>Unfortunately, you can’t set the grinder to whatever step you please. As the machine recommends you a grind setting, you can’t just go rogue and do what you want. As a result, I was only able to do a portion of my “normal” grinder testing. Obviously, if you’re making “drip”, the machine will suggest a much coarser grind size. </p><p>You get a dosing collar that slots into the grinder mechanism to prevent mess, too.</p><p>Normally, I would go through every fineness: Turkish all the way to cold brew. Here, though, I was only able to test the espresso-fine 12-step.</p><p>Here’s my standard uniformity testing. I’m looking for big jumps here: I want 0% to 90%. This proves the grinder is uniform, as all the grounds are the same size. </p><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>>500um</strong></p></th><th  ><p><strong><500um</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p>Click 12</p></td><td  ><p>60%</p></td><td  ><p>40%</p></td></tr></tbody></table></div><p>I would have liked to do more testing — but as I said, the machine only lets you grind on its recommended size!</p><p>As you can see, the grinder isn’t overly uniform. However, dialing in is super easy, as the machine does everything for you. No weighing or dosing or adjusting the grind. The machine takes care of all that headache. </p><h2 id="espresso-3">Espresso</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="yqhii2a2btUL4NbVn5JRyG" name="Ninja Luxe Cafe Mini Plus 6.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/yqhii2a2btUL4NbVn5JRyG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Ninja’s espresso tastes fine. It’s not delicious, but it’s not disgusting, either. If you’re the kind of person who puts syrups or flavoring in your coffee, you probably won’t notice the taste flavor at all — but isn’t that kind of the point? The espresso tastes like coffee. Not massively nuanced or bright coffee, but coffee. It tastes like Starbucks. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2596px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="vvSbcsZqwEQG8Dtsz6BFV9" name="ninja luxe cafe mini plus espresso" alt="a photo of an espresso shot made on the ninja luxe cafe mini plus" src="https://cdn.mos.cms.futurecdn.net/vvSbcsZqwEQG8Dtsz6BFV9.jpg" mos="" align="middle" fullscreen="" width="2596" height="1460" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the shot is a nice caramel color with a layer of crema. The crema isn’t as creamy as I’d like, but it still tasted good. There was no nuance or complexity to the shot: it tasted like chain coffee. However, this is still good enough for most people — and without the dialing-in headache. It brewed 34g of coffee in 26 seconds. </p><p>I will note that the portafilter is tiny — just 53 mm. This means you likely won’t be able to get <em>much</em> aftermarket stuff in terms of accessories, but, again, I’m not sure that’s who this machine is aimed at. You do, however, get a dosing collar and a spring-loaded tamper as standard.</p><p>As I mentioned earlier, while the grinder isn’t “technically” perfect in the same way you’d expect from a standalone grinder, it’s fine for casual coffee drinkers who prioritize ease of use over coffee snobbery. </p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-milk"><span>Ninja Luxe Cafe Mini Plus review: Milk</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kxyUpQjdGGdeEKESW3Y5zG" name="Ninja Luxe Cafe Mini Plus 5.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/kxyUpQjdGGdeEKESW3Y5zG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Luxe Cafe Mini has two milk steaming modes: automatic and manual. You can choose from three levels of foam: low, medium, and high. Weirdly, the “medium” foam level is more like a cappuccino. It took 1 minute 49 seconds to heat a jug of milk to drinking temperature, which is longer than the Breville Bambino Plus’s auto milk frothing time. </p><p>Manual milk steaming works the same as any other espresso machine. I was surprised to see the Luxe Cafe Mini Plus has a super strong steam wand — much stronger than Casabrews alternatives I’ve tested. It’s around the same strength as a De’Longhi Arte Evo. </p><p>Here’s a photo of automatically frothed milk. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3673px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8QDySNkQXj9fBjdKBvZZnD" name="ninja luxe cafe mini plus auto milk" alt="a photo of auto milk made on the ninja luxe cafe mini plus" src="https://cdn.mos.cms.futurecdn.net/8QDySNkQXj9fBjdKBvZZnD.jpg" mos="" align="middle" fullscreen="" width="3673" height="2066" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the texture is okay. It’s not perfect, but it’s soft enough to achieve some latte art. There was a disconnect between the milk and froth, which resulted in a dodgy pour. I’d recommend serious home baristas foam their own milk, but newbies might find this super helpful.</p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-storage-maintenance"><span>Ninja Luxe Cafe Mini Plus review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NKYdZ7LBrfMEa6SUVjpGwG" name="Ninja Luxe Cafe Mini Plus 9.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/NKYdZ7LBrfMEa6SUVjpGwG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As the Ninja Luxe Cafe Mini Plus is, well, <em>mini</em>, it’s a better option for smaller spaces. Although it’s not as small as a standalone espresso machine, it’s smaller than alternative bean-to-cups. Let’s get our comparison caps on! N.B.: the last model, Breville Bambino, is a standalone espresso machine. This is just for a baseline comparison as the Bambino is <em>the</em> small machine of choice for many. I’m aware they serve slightly different audiences.</p><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p>Height</p></th><th  ><p>Width</p></th><th  ><p>Depth</p></th><th  ><p>Weight</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Ninja Luxe Cafe Mini Plus</strong></p></td><td  ><p>13.43 inches</p></td><td  ><p>12.17 inches</p></td><td  ><p>10.24 inches</p></td><td  ><p>18.96 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Ninja Luxe Cafe Pro</strong></p></td><td  ><p>15.20 inches</p></td><td  ><p>12.50 inches</p></td><td  ><p>13.40 inches</p></td><td  ><p>27.10 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Ninja Luxe Cafe Premier</strong></p></td><td  ><p>14.65 inches</p></td><td  ><p>13.23 inches</p></td><td  ><p>13.54 inches</p></td><td  ><p>37.50 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Breville Barista Express</strong></p></td><td  ><p>15.90 inches</p></td><td  ><p>12.50 inches</p></td><td  ><p>13.80 inches</p></td><td  ><p>22.10 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>De'Longhi La Specialista Arte Evo</strong></p></td><td  ><p>15.70 inches</p></td><td  ><p>11.20 inches</p></td><td  ><p>14.40 inches</p></td><td  ><p>20.50 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Breville Oracle Dual Boiler</strong></p></td><td  ><p>14.7 inches</p></td><td  ><p>17.9 inches</p></td><td  ><p>15.2 inches</p></td><td  ><p>38 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Breville Bambino</strong></p></td><td  ><p>12.20 inches</p></td><td  ><p>7.50 inches</p></td><td  ><p>12.60 inches</p></td><td  ><p>10.80 pounds</p></td></tr></tbody></table></div><p>As you can see, the Luxe Cafe Mini is considerably lighter than the other bean-to-cups. It’s also shorter and slimmer than all the others, but the De’Longhi Arte Evo is narrower. </p><p>I have no qualms about recommending the Luxe Cafe Mini to those who need a smaller espresso machine but don’t want to sacrifice a built-in grinder. </p><p>In terms of longevity, Ninja offers a 2-year warranty, which is the same as Breville and De’Longhi, so pretty standard. As the machine doesn’t come with cleaning products, you’d need to get backflush tablets and descaler yourself. These are <a href="https://www.sharkninja.com/espresso-cleaning-supply-kit/XSKCLRKT4MNSUP.html">$34 for a 4-month supply directly from Ninja</a>. It does come with a water filter, though, which is nice!</p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-verdict"><span>Ninja Luxe Cafe Mini Plus review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="c49ibzH8pvUpGRL38UdsxG" name="Ninja Luxe Cafe Mini Plus 10.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/c49ibzH8pvUpGRL38UdsxG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Ninja Luxe Cafe Mini Plus is a fantastic first espresso machine. I won’t impress coffee snobs, but it will hit the spot if you value ease of use, simplicity, and consistency. The automatic dial-in process is perhaps <em>the</em> best thing for newbies. No stressing over why no coffee came out or why your coffee tastes like sour bean water. You do get a decent shot of espresso every time. </p><p>No, there’s no nuance of complexity in the shot. I use the same beans for all my testing, and they tasted like Starbucks rather than the snobby shop down the road. But is that a bad thing? Starbucks is popular for a reason. People love coffee that tastes like coffee. I personally wouldn’t get this machine, but I’m a coffee snob. Take what I say with a pinch of salt.</p><p>If you’re reading this and nodding along, you’re definitely the target audience for the Ninja Luxe Cafe Mini Plus. It helps you make consistent espresso and milk. Coffee lovers looking for a one-ticket route to consistently great coffee? Ninja Luxe Cafe Mini Plus.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/ninja-luxe-cafe-mini-plus-review</link>
                                                                            <description>
                            <![CDATA[ The Ninja Luxe Cafe Mini Plus is one of the best espresso machines for complete beginners due to its helpful dialing-in assist feature. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">FRThs2fus99unKbhTnGLFf</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/YmZ5ntmeemebZj4nkGAbwG-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 24 Jul 2026 12:42:07 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/YmZ5ntmeemebZj4nkGAbwG-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the ninja luxe cafe mini plus photographed against the blue tom&#039;s guide background]]></media:description>                                                            <media:text><![CDATA[the ninja luxe cafe mini plus photographed against the blue tom&#039;s guide background]]></media:text>
                                <media:title type="plain"><![CDATA[the ninja luxe cafe mini plus photographed against the blue tom&#039;s guide background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/YmZ5ntmeemebZj4nkGAbwG-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Ninja Luxe Cafe Mini Plus does the one thing I know most new home baristas struggle with: dialing in. Ever wondered why nothing is coming out of your espresso machine, or it’s coming out like water from a newly unblocked pipe? It’s because you haven’t dialed your coffee in yet. </p><p>But Ninja said ‘No more!’ No longer do you have to waste precious coffee on figuring out optimal grind size’ The Luxe Cafe Mini Plus has auto-grind-size calibration that tells you the precise “click” you should grind your beans at. This is genuinely <em>so</em> helpful for newbies.</p><p>So, is this enough to make the Ninja Luxe Cafe Mini Plus one of the <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">best espresso machines</a>? For beginners, totally. Say goodbye to coffee-related headaches (unless you’re slamming double espresso after double espresso, but that’s a completely different type of headache). Want to find out more? Why wouldn’t you? Keep reading this Ninja Luxe Cafe Mini Plus review. </p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-specs"><span>Ninja Luxe Cafe Mini Plus review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Ninja-Precision-Cappuccinos-Stainless-ES301/dp/B0GVTV3QTR" target="_blank" rel="nofollow">$499</a> / <a href="https://www.amazon.co.uk/Ninja-Espresso-Machine-Automatic-ES351UK/dp/B0GXB9XN2P" target="_blank" rel="nofollow">£499 </a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>19 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>10.2 x 12.2 x 13.4 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Grinder</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Heating system </strong></p></td><td  ><p>Not known</p></td></tr><tr><td class="firstcol " ><p><strong>Pressure </strong></p></td><td  ><p>Not known</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank capacity</strong></p></td><td  ><p>44 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>Milk jug</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-price-availability"><span>Ninja Luxe Cafe Mini Plus review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4LkvELxSJJcegS2zUSmzxG" name="Ninja Luxe Cafe Mini Plus 8.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/4LkvELxSJJcegS2zUSmzxG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Ninja Luxe Cafe Mini Plus is <a href="https://www.sharkninja.com/ninja-luxe-cafe-mini-plus-autofroth-espresso-machine/ES351.html?dwvar_ES351_color=dbdbdb" target="_blank" rel="nofollow">$499 from SharkNinja U.S.</a> and <a href="https://www.amazon.co.uk/Ninja-Espresso-Machine-Automatic-ES351UK/dp/B0GXB9XN2P" target="_blank" rel="nofollow">£499 from Amazon U.K.</a>. Although this looks like an objectively large amount of money, I can assure you this is actually reasonable for an espresso machine with a built-in grinder. </p><p>Ninja also makes the <a href="https://www.amazon.com/Ninja-Precision-Cappuccinos-Stainless-ES301/dp/B0GVTV3QTR" target="_blank" rel="nofollow">Luxe Cafe Mini </a>($459). The Mini doesn’t have automatic milk frothing. On the Luxe Cafe lineup is the <a href="https://www.tomsguide.com/home/coffee-makers/ninja-luxe-cafe-pro-espresso-machine-review">Luxe Cafe Pro</a> ($749), Luxe Cafe Premier ($599), and then you’ve got the eye-watering <a href="https://www.tomsguide.com/home/coffee-makers/ninja-auto-barista-pro-review">AutoBarista Pro</a> ($949). </p><p>The cheapest setup I’d recommend is the <a href="https://www.tomsguide.com/home/coffee-makers/casabrews-3700-essential-review">Casabrews 3700 Essential </a>($129) and the <a href="https://www.tomsguide.com/home/kitchen-dining/timemore-chestnut-c2s-review">Timemore C2s Chestnut</a> ($60), which works out to $189. However, this is a hand grinder, so it might not be best for those who have no desire to spend ten minutes turning a crank (me included — I make my boyfriend grind my beans). </p><p>When you get into bean-to-cups, the price increases dramatically. You’ve got the Casabrews Marenza ($349), the cheapest bean-to-cup I’ve ever reviewed, but there’s a catch — the grinder hopper smashed after the second use, so I wouldn’t recommend it for longevity. </p><p>You’ve also got the <a href="https://www.tomsguide.com/reviews/breville-barista-express-espresso-machine">Breville Barista Express</a> ($699), one of the most iconic bean-to-cups ever made, and the <a href="https://www.tomsguide.com/home/coffee-makers/delonghi-la-specialista-arte-evo-review">De’Longhi La Specialista Arte Evo</a> ($699), the best-value bean-to-cup I’ve reviewed. The Luxe Cafe Mini Plus undercuts both by some margin.</p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-design"><span>Ninja Luxe Cafe Mini Plus review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CvCpiLCfATsdnrnKe6B2jG" name="Ninja Luxe Cafe Mini Plus 3.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/CvCpiLCfATsdnrnKe6B2jG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Just like the Ninja AutoBarista Pro I reviewed last month, the Luxe Cafe Mini is effectively a big plastic square. The machine gets the job done, but I don’t think it’s as pretty as the Breville Barista Express, De’Longhi Arte Evo, <a href="https://www.tomsguide.com/home/coffee-makers/lelit-mara-x3-review">Lelit Mara X3</a>, or the <a href="https://www.tomsguide.com/home/coffee-makers/fellow-espresso-series-1-review">Fellow Espresso Series 1</a>. </p><p>Of course, though, beauty is in the eye of the beholder, and you may totally disagree. That’s fine! Ninja knows what works, obviously; it wouldn’t keep releasing espresso machines with the same functional aesthetic otherwise. It must work.</p><p>What separates the Ninja Luxe Cafe Mini Plus from the Luxe Cafe Pro or AutoBarista Pro is pretty obvious — its size. The Luxe Cafe Mini is 3 inches narrower, half an inch slimmer, and 1 inch lower than the Luxe Cafe Pro. I’d recommend it for small spaces any day. </p><p>There’s an LCD screen on the front of the machine: you can see your grind size and coffee settings here. It’s easy to change said settings: just twist the knob and press the buttons. They’re all labeled, and it’s practically impossible to go wrong here. </p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-espresso"><span>Ninja Luxe Cafe Mini Plus review: Espresso</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AQEozpYzkWHx2mu9KKQivG" name="Ninja Luxe Cafe Mini Plus 2.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/AQEozpYzkWHx2mu9KKQivG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As this is a bean-to-cup, I’ll discuss grinder and espresso here. I’ll go through milk performance later. The grinder operates at 71 dB, which is around the same volume as a running shower or a flushing toilet. As it only grinds for a few seconds, I’d say this is pretty quiet, all things considered. </p><h2 id="grinder-2">Grinder</h2><p>Unfortunately, you can’t set the grinder to whatever step you please. As the machine recommends you a grind setting, you can’t just go rogue and do what you want. As a result, I was only able to do a portion of my “normal” grinder testing. Obviously, if you’re making “drip”, the machine will suggest a much coarser grind size. </p><p>You get a dosing collar that slots into the grinder mechanism to prevent mess, too.</p><p>Normally, I would go through every fineness: Turkish all the way to cold brew. Here, though, I was only able to test the espresso-fine 12-step.</p><p>Here’s my standard uniformity testing. I’m looking for big jumps here: I want 0% to 90%. This proves the grinder is uniform, as all the grounds are the same size. </p><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>>500um</strong></p></th><th  ><p><strong><500um</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p>Click 12</p></td><td  ><p>60%</p></td><td  ><p>40%</p></td></tr></tbody></table></div><p>I would have liked to do more testing — but as I said, the machine only lets you grind on its recommended size!</p><p>As you can see, the grinder isn’t overly uniform. However, dialing in is super easy, as the machine does everything for you. No weighing or dosing or adjusting the grind. The machine takes care of all that headache. </p><h2 id="espresso-3">Espresso</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="yqhii2a2btUL4NbVn5JRyG" name="Ninja Luxe Cafe Mini Plus 6.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/yqhii2a2btUL4NbVn5JRyG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Ninja’s espresso tastes fine. It’s not delicious, but it’s not disgusting, either. If you’re the kind of person who puts syrups or flavoring in your coffee, you probably won’t notice the taste flavor at all — but isn’t that kind of the point? The espresso tastes like coffee. Not massively nuanced or bright coffee, but coffee. It tastes like Starbucks. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2596px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="vvSbcsZqwEQG8Dtsz6BFV9" name="ninja luxe cafe mini plus espresso" alt="a photo of an espresso shot made on the ninja luxe cafe mini plus" src="https://cdn.mos.cms.futurecdn.net/vvSbcsZqwEQG8Dtsz6BFV9.jpg" mos="" align="middle" fullscreen="" width="2596" height="1460" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the shot is a nice caramel color with a layer of crema. The crema isn’t as creamy as I’d like, but it still tasted good. There was no nuance or complexity to the shot: it tasted like chain coffee. However, this is still good enough for most people — and without the dialing-in headache. It brewed 34g of coffee in 26 seconds. </p><p>I will note that the portafilter is tiny — just 53 mm. This means you likely won’t be able to get <em>much</em> aftermarket stuff in terms of accessories, but, again, I’m not sure that’s who this machine is aimed at. You do, however, get a dosing collar and a spring-loaded tamper as standard.</p><p>As I mentioned earlier, while the grinder isn’t “technically” perfect in the same way you’d expect from a standalone grinder, it’s fine for casual coffee drinkers who prioritize ease of use over coffee snobbery. </p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-milk"><span>Ninja Luxe Cafe Mini Plus review: Milk</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kxyUpQjdGGdeEKESW3Y5zG" name="Ninja Luxe Cafe Mini Plus 5.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/kxyUpQjdGGdeEKESW3Y5zG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Luxe Cafe Mini has two milk steaming modes: automatic and manual. You can choose from three levels of foam: low, medium, and high. Weirdly, the “medium” foam level is more like a cappuccino. It took 1 minute 49 seconds to heat a jug of milk to drinking temperature, which is longer than the Breville Bambino Plus’s auto milk frothing time. </p><p>Manual milk steaming works the same as any other espresso machine. I was surprised to see the Luxe Cafe Mini Plus has a super strong steam wand — much stronger than Casabrews alternatives I’ve tested. It’s around the same strength as a De’Longhi Arte Evo. </p><p>Here’s a photo of automatically frothed milk. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3673px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8QDySNkQXj9fBjdKBvZZnD" name="ninja luxe cafe mini plus auto milk" alt="a photo of auto milk made on the ninja luxe cafe mini plus" src="https://cdn.mos.cms.futurecdn.net/8QDySNkQXj9fBjdKBvZZnD.jpg" mos="" align="middle" fullscreen="" width="3673" height="2066" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As you can see, the texture is okay. It’s not perfect, but it’s soft enough to achieve some latte art. There was a disconnect between the milk and froth, which resulted in a dodgy pour. I’d recommend serious home baristas foam their own milk, but newbies might find this super helpful.</p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-storage-maintenance"><span>Ninja Luxe Cafe Mini Plus review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NKYdZ7LBrfMEa6SUVjpGwG" name="Ninja Luxe Cafe Mini Plus 9.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/NKYdZ7LBrfMEa6SUVjpGwG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As the Ninja Luxe Cafe Mini Plus is, well, <em>mini</em>, it’s a better option for smaller spaces. Although it’s not as small as a standalone espresso machine, it’s smaller than alternative bean-to-cups. Let’s get our comparison caps on! N.B.: the last model, Breville Bambino, is a standalone espresso machine. This is just for a baseline comparison as the Bambino is <em>the</em> small machine of choice for many. I’m aware they serve slightly different audiences.</p><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p>Height</p></th><th  ><p>Width</p></th><th  ><p>Depth</p></th><th  ><p>Weight</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Ninja Luxe Cafe Mini Plus</strong></p></td><td  ><p>13.43 inches</p></td><td  ><p>12.17 inches</p></td><td  ><p>10.24 inches</p></td><td  ><p>18.96 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Ninja Luxe Cafe Pro</strong></p></td><td  ><p>15.20 inches</p></td><td  ><p>12.50 inches</p></td><td  ><p>13.40 inches</p></td><td  ><p>27.10 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Ninja Luxe Cafe Premier</strong></p></td><td  ><p>14.65 inches</p></td><td  ><p>13.23 inches</p></td><td  ><p>13.54 inches</p></td><td  ><p>37.50 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Breville Barista Express</strong></p></td><td  ><p>15.90 inches</p></td><td  ><p>12.50 inches</p></td><td  ><p>13.80 inches</p></td><td  ><p>22.10 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>De'Longhi La Specialista Arte Evo</strong></p></td><td  ><p>15.70 inches</p></td><td  ><p>11.20 inches</p></td><td  ><p>14.40 inches</p></td><td  ><p>20.50 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Breville Oracle Dual Boiler</strong></p></td><td  ><p>14.7 inches</p></td><td  ><p>17.9 inches</p></td><td  ><p>15.2 inches</p></td><td  ><p>38 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Breville Bambino</strong></p></td><td  ><p>12.20 inches</p></td><td  ><p>7.50 inches</p></td><td  ><p>12.60 inches</p></td><td  ><p>10.80 pounds</p></td></tr></tbody></table></div><p>As you can see, the Luxe Cafe Mini is considerably lighter than the other bean-to-cups. It’s also shorter and slimmer than all the others, but the De’Longhi Arte Evo is narrower. </p><p>I have no qualms about recommending the Luxe Cafe Mini to those who need a smaller espresso machine but don’t want to sacrifice a built-in grinder. </p><p>In terms of longevity, Ninja offers a 2-year warranty, which is the same as Breville and De’Longhi, so pretty standard. As the machine doesn’t come with cleaning products, you’d need to get backflush tablets and descaler yourself. These are <a href="https://www.sharkninja.com/espresso-cleaning-supply-kit/XSKCLRKT4MNSUP.html">$34 for a 4-month supply directly from Ninja</a>. It does come with a water filter, though, which is nice!</p><h2 class="article-body__section" id="section-ninja-luxe-cafe-mini-plus-review-verdict"><span>Ninja Luxe Cafe Mini Plus review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="c49ibzH8pvUpGRL38UdsxG" name="Ninja Luxe Cafe Mini Plus 10.JPG" alt="the ninja luxe cafe mini plus photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/c49ibzH8pvUpGRL38UdsxG.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Ninja Luxe Cafe Mini Plus is a fantastic first espresso machine. I won’t impress coffee snobs, but it will hit the spot if you value ease of use, simplicity, and consistency. The automatic dial-in process is perhaps <em>the</em> best thing for newbies. No stressing over why no coffee came out or why your coffee tastes like sour bean water. You do get a decent shot of espresso every time. </p><p>No, there’s no nuance of complexity in the shot. I use the same beans for all my testing, and they tasted like Starbucks rather than the snobby shop down the road. But is that a bad thing? Starbucks is popular for a reason. People love coffee that tastes like coffee. I personally wouldn’t get this machine, but I’m a coffee snob. Take what I say with a pinch of salt.</p><p>If you’re reading this and nodding along, you’re definitely the target audience for the Ninja Luxe Cafe Mini Plus. It helps you make consistent espresso and milk. Coffee lovers looking for a one-ticket route to consistently great coffee? Ninja Luxe Cafe Mini Plus.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ You said your Moccamaster couldn't do these 3 things — here's how I'd fix it ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the sixteenth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>The <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster</a> is one of my <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers">favorite coffee makers</a>. No, I'd actually say it's my favorite. It has everything I like: a cool vibe, easy functionality, and consistent taste. Oh, and the best part? The lifetime repairs and 5-year standard warranty. Honestly, there's nothing I <em>don't</em> like about the Moccamaster. </p><p>But I know some of you have questions about the Moccamaster, and I'm here to answer them! I got <em>tons</em> of comments on TikTok, so I'm answering the three most-commonly asked questions. </p><p>So, if you want to know how I'd fix the Moccamaster having "too much plastic," the Moccamaster's "horrible water distribution," or the Moccamaster "hot plate burning the coffee," buckle in.</p><h2 id="1-you-said-the-moccamaster-has-too-much-plastic">1. You said the Moccamaster has "too much plastic"</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="WcDTsK5MNkH3R5F5Q8uVME" name="Simply Good Coffee Plastic-Free Brewer 17.JPG" alt="the simply good coffee plastic-free brewer photographed against the blue tom's guide backgrounds" src="https://cdn.mos.cms.futurecdn.net/WcDTsK5MNkH3R5F5Q8uVME.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I both agree and disagree! Yes, it has a lot of plastic, but due to E.U. regulations, the Moccamaster only uses virgin, BPA-free, phthalate-free plastic, so none of this plastic will leach into your coffee. You also need to remember that Technivorm makes the Moccamaster in the Netherlands, and shipping a completely glass/metal coffee maker across the Atlantic would cost a pretty penny... and the Moccamaster <em>already </em>costs a pretty penny.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eBzGKe"></div>                            </div>                            <script src="https://kwizly.com/embed/eBzGKe.js" async></script><p>However, if you want as little plastic in your kitchen as possible — I don't blame you — then you might be a little more interested in the Simply Good Coffee Plastic-Free Brewer. I reviewed this coffee maker a month or so ago and I loved it. As it has a bloom feature, it makes a juicier, fruitier coffee, more attuned to what I'd describe as "coffee-snob coffee". </p><p>Although the SGC Brewer still has silicone and plastic on the exterior to prevent burns, it's much less plastic than the Moccamaster. </p><div class="product"><a data-dimension112="905fc6b4-80ee-11f1-bab2-0df42783f275" data-action="Deal Block" data-label="Plastic-Free Brewer" data-dimension48="Plastic-Free Brewer" data-dimension25="$479" href="https://www.amazon.com/dp/B0FLYM8986" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="kBwcCTJrsdvHBBcz4LZYMa" name="Plastic-Free Brewer" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kBwcCTJrsdvHBBcz4LZYMa.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Simply Good Plastic-Free Brewer measures 13.7 x 7.8 x 14.6 inches, uses a #4 cone, and can brew up to 8 cups of coffee at a time. Oh, and there's no plastic in the hot water brewing process whatsoever.</p><p><strong>Warranty check</strong>: 5 years as standard.<a class="view-deal button" href="https://www.amazon.com/dp/B0FLYM8986" target="_blank" rel="nofollow" data-dimension112="905fc6b4-80ee-11f1-bab2-0df42783f275" data-action="Deal Block" data-label="Plastic-Free Brewer" data-dimension48="Plastic-Free Brewer" data-dimension25="$479">View Deal</a></p></div><h2 id="2-you-said-the-moccamaster-has-horrible-water-distribution">2. You said the Moccamaster has "horrible water distribution"</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9rszCagDgaKiGuWkSKWWGV" name="Technivorm Moccamaster KGBV Select 8.JPG" alt="the technivorm moccamaster kgbv select in pink photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/9rszCagDgaKiGuWkSKWWGV.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I also agree and disagree with this one. The Moccamaster does tend to "funnel" — where the water goes down the center of the filter basket and the coffee grounds climb up the side, resulting in overextracted and underextracted flavors. </p><p>However, this is less a Moccamaster issue and more a drip coffee maker issue. If you want drip, it's an occupational hazard. I've yet to use a drip coffee maker with zero funnelling. Even the "coffee-snob-approved" SGC Plastic-Free Brewer doesn't manage to distribute the water as evenly as a well-poured pour-over. </p><div class="product"><a data-dimension112="d2c1ed02-80ee-11f1-b1c5-ad9071c17405" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$350" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2297px;"><p class="vanilla-image-block" style="padding-top:108.84%;"><img id="ViCKNPMdH4jYKkGFaBFznF" name="moccamaster_sorbet_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ViCKNPMdH4jYKkGFaBFznF.jpg" mos="" align="middle" fullscreen="" width="2297" height="2500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="d2c1ed02-80ee-11f1-b1c5-ad9071c17405" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$350">Moccamaster KGBV Select: was $369 now $350</a></strong><br>There's a reason why the <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster</a> is so revered in the coffee world: it seriously is that good. It's super easy to use, SCA-accredited (for both full-batch and half-batch), and makes full-bodied-but-not-bitter coffee.  </p><p><strong>Warranty check:</strong> 5 years as standard and <em>lifetime repairs</em>.<a class="view-deal button" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="d2c1ed02-80ee-11f1-b1c5-ad9071c17405" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$350">View Deal</a></p></div><p>You can easily mitigate this, though. You need to grind all your coffee evenly using a good grinder. I'd recommend something like the <a href="https://www.tomsguide.com/home/home-appliances/baratza-encore-esp-review">Baratza Encore ESP</a> if you can afford $199 or the <a href="https://www.tomsguide.com/home/kitchen-dining/timemore-chestnut-c2s-review">Timemore Chestnut C2s</a> if you don't mind hand grinding and you only want to spend $60.</p><p>Next: make sure your grind isn't too fine or too coarse. A coarser grind may be more inclined to funnel as there's more surface area for the water to burrow under. Too fine will create blockages. </p><p>Finally, this is what I would do after exhausting all other options. When you brew, don't put on the filter basket lid. Manually "bloom" the grounds. This means add a little bit of water, then stir, ensuring the water has saturated the grounds. Then, let the machine drip as normal. See the difference? </p><h2 id="you-said-the-moccamaster-hot-plate-burns-the-coffee">You said the Moccamaster hot plate "burns the coffee"</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6hmSMxLWQGUsvAHpeSGN7V" name="Technivorm Moccamaster KGBV Select 7.JPG" alt="the technivorm moccamaster kgbv select in pink photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/6hmSMxLWQGUsvAHpeSGN7V.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>And I agree with you! The hot plate does burn the coffee. I never use the hot plate feature on mine. I always make a half batch if I'm brewing just for me, or make sure everyone gets a cup while it's fresh. </p><p>If you want to keep your coffee hot without burning, I'd recommend checking out <a href="https://www.amazon.com/Moccamaster-59865-Thermos-Stainless-Steel/dp/B0BGPFVNRY" target="_blank" rel="nofollow">Technivorm's thermal carafe, which is $84 from Amazon</a>.</p><p>So, that's everything you asked me this week! Next week, I'll be answering more questions about the Moccamaster.</p><p>I release new episodes of The Coffee Lab every Friday on our TikTok, Instagram, YouTube, and also here on Tom's Guide.</p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-the-new-nespresso-summer-flavors-heres-my-favorite"><strong>I tried the new Nespresso summer flavors — here's my favorite</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/non-plastic-coffee-makers-are-trending-and-this-low-plastic-ratio-model-is-my-favorite-for-coffee-snobs"><strong>Non-plastic coffee makers are trending — and this low-plastic Ratio model is my favorite for coffee snobs</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/you-said-your-moccamaster-couldnt-do-these-3-things-heres-how-id-fix-it</link>
                                                                            <description>
                            <![CDATA[ On the sixteenth episode of The Coffee Lab, I answer your most-asked questions about the Moccamaster. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">wJpG2vxebUFB8bqzkRafkH</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/ExcBtgvWh3S5uaZ7fgXxQX-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Fri, 17 Jul 2026 12:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/png" url="https://cdn.mos.cms.futurecdn.net/ExcBtgvWh3S5uaZ7fgXxQX-1280-80.png">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the moccamaster with the coffee lab]]></media:description>                                                            <media:text><![CDATA[the moccamaster with the coffee lab]]></media:text>
                                <media:title type="plain"><![CDATA[the moccamaster with the coffee lab]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/ExcBtgvWh3S5uaZ7fgXxQX-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the sixteenth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>The <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster</a> is one of my <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers">favorite coffee makers</a>. No, I'd actually say it's my favorite. It has everything I like: a cool vibe, easy functionality, and consistent taste. Oh, and the best part? The lifetime repairs and 5-year standard warranty. Honestly, there's nothing I <em>don't</em> like about the Moccamaster. </p><p>But I know some of you have questions about the Moccamaster, and I'm here to answer them! I got <em>tons</em> of comments on TikTok, so I'm answering the three most-commonly asked questions. </p><p>So, if you want to know how I'd fix the Moccamaster having "too much plastic," the Moccamaster's "horrible water distribution," or the Moccamaster "hot plate burning the coffee," buckle in.</p><h2 id="1-you-said-the-moccamaster-has-too-much-plastic">1. You said the Moccamaster has "too much plastic"</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="WcDTsK5MNkH3R5F5Q8uVME" name="Simply Good Coffee Plastic-Free Brewer 17.JPG" alt="the simply good coffee plastic-free brewer photographed against the blue tom's guide backgrounds" src="https://cdn.mos.cms.futurecdn.net/WcDTsK5MNkH3R5F5Q8uVME.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I both agree and disagree! Yes, it has a lot of plastic, but due to E.U. regulations, the Moccamaster only uses virgin, BPA-free, phthalate-free plastic, so none of this plastic will leach into your coffee. You also need to remember that Technivorm makes the Moccamaster in the Netherlands, and shipping a completely glass/metal coffee maker across the Atlantic would cost a pretty penny... and the Moccamaster <em>already </em>costs a pretty penny.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eBzGKe"></div>                            </div>                            <script src="https://kwizly.com/embed/eBzGKe.js" async></script><p>However, if you want as little plastic in your kitchen as possible — I don't blame you — then you might be a little more interested in the Simply Good Coffee Plastic-Free Brewer. I reviewed this coffee maker a month or so ago and I loved it. As it has a bloom feature, it makes a juicier, fruitier coffee, more attuned to what I'd describe as "coffee-snob coffee". </p><p>Although the SGC Brewer still has silicone and plastic on the exterior to prevent burns, it's much less plastic than the Moccamaster. </p><div class="product"><a data-dimension112="905fc6b4-80ee-11f1-bab2-0df42783f275" data-action="Deal Block" data-label="Plastic-Free Brewer" data-dimension48="Plastic-Free Brewer" data-dimension25="$479" href="https://www.amazon.com/dp/B0FLYM8986" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="kBwcCTJrsdvHBBcz4LZYMa" name="Plastic-Free Brewer" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kBwcCTJrsdvHBBcz4LZYMa.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Simply Good Plastic-Free Brewer measures 13.7 x 7.8 x 14.6 inches, uses a #4 cone, and can brew up to 8 cups of coffee at a time. Oh, and there's no plastic in the hot water brewing process whatsoever.</p><p><strong>Warranty check</strong>: 5 years as standard.<a class="view-deal button" href="https://www.amazon.com/dp/B0FLYM8986" target="_blank" rel="nofollow" data-dimension112="905fc6b4-80ee-11f1-bab2-0df42783f275" data-action="Deal Block" data-label="Plastic-Free Brewer" data-dimension48="Plastic-Free Brewer" data-dimension25="$479">View Deal</a></p></div><h2 id="2-you-said-the-moccamaster-has-horrible-water-distribution">2. You said the Moccamaster has "horrible water distribution"</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9rszCagDgaKiGuWkSKWWGV" name="Technivorm Moccamaster KGBV Select 8.JPG" alt="the technivorm moccamaster kgbv select in pink photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/9rszCagDgaKiGuWkSKWWGV.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I also agree and disagree with this one. The Moccamaster does tend to "funnel" — where the water goes down the center of the filter basket and the coffee grounds climb up the side, resulting in overextracted and underextracted flavors. </p><p>However, this is less a Moccamaster issue and more a drip coffee maker issue. If you want drip, it's an occupational hazard. I've yet to use a drip coffee maker with zero funnelling. Even the "coffee-snob-approved" SGC Plastic-Free Brewer doesn't manage to distribute the water as evenly as a well-poured pour-over. </p><div class="product"><a data-dimension112="d2c1ed02-80ee-11f1-b1c5-ad9071c17405" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$350" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2297px;"><p class="vanilla-image-block" style="padding-top:108.84%;"><img id="ViCKNPMdH4jYKkGFaBFznF" name="moccamaster_sorbet_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ViCKNPMdH4jYKkGFaBFznF.jpg" mos="" align="middle" fullscreen="" width="2297" height="2500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="d2c1ed02-80ee-11f1-b1c5-ad9071c17405" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$350">Moccamaster KGBV Select: was $369 now $350</a></strong><br>There's a reason why the <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">Moccamaster</a> is so revered in the coffee world: it seriously is that good. It's super easy to use, SCA-accredited (for both full-batch and half-batch), and makes full-bodied-but-not-bitter coffee.  </p><p><strong>Warranty check:</strong> 5 years as standard and <em>lifetime repairs</em>.<a class="view-deal button" href="https://www.amazon.com/Moccamaster-53941-Select-10-Cup-Polished/dp/B093DYPBYR" target="_blank" rel="nofollow" data-dimension112="d2c1ed02-80ee-11f1-b1c5-ad9071c17405" data-action="Deal Block" data-label="Moccamaster KGBV Select" data-dimension48="Moccamaster KGBV Select" data-dimension25="$350">View Deal</a></p></div><p>You can easily mitigate this, though. You need to grind all your coffee evenly using a good grinder. I'd recommend something like the <a href="https://www.tomsguide.com/home/home-appliances/baratza-encore-esp-review">Baratza Encore ESP</a> if you can afford $199 or the <a href="https://www.tomsguide.com/home/kitchen-dining/timemore-chestnut-c2s-review">Timemore Chestnut C2s</a> if you don't mind hand grinding and you only want to spend $60.</p><p>Next: make sure your grind isn't too fine or too coarse. A coarser grind may be more inclined to funnel as there's more surface area for the water to burrow under. Too fine will create blockages. </p><p>Finally, this is what I would do after exhausting all other options. When you brew, don't put on the filter basket lid. Manually "bloom" the grounds. This means add a little bit of water, then stir, ensuring the water has saturated the grounds. Then, let the machine drip as normal. See the difference? </p><h2 id="you-said-the-moccamaster-hot-plate-burns-the-coffee">You said the Moccamaster hot plate "burns the coffee"</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6hmSMxLWQGUsvAHpeSGN7V" name="Technivorm Moccamaster KGBV Select 7.JPG" alt="the technivorm moccamaster kgbv select in pink photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/6hmSMxLWQGUsvAHpeSGN7V.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>And I agree with you! The hot plate does burn the coffee. I never use the hot plate feature on mine. I always make a half batch if I'm brewing just for me, or make sure everyone gets a cup while it's fresh. </p><p>If you want to keep your coffee hot without burning, I'd recommend checking out <a href="https://www.amazon.com/Moccamaster-59865-Thermos-Stainless-Steel/dp/B0BGPFVNRY" target="_blank" rel="nofollow">Technivorm's thermal carafe, which is $84 from Amazon</a>.</p><p>So, that's everything you asked me this week! Next week, I'll be answering more questions about the Moccamaster.</p><p>I release new episodes of The Coffee Lab every Friday on our TikTok, Instagram, YouTube, and also here on Tom's Guide.</p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-the-new-nespresso-summer-flavors-heres-my-favorite"><strong>I tried the new Nespresso summer flavors — here's my favorite</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/non-plastic-coffee-makers-are-trending-and-this-low-plastic-ratio-model-is-my-favorite-for-coffee-snobs"><strong>Non-plastic coffee makers are trending — and this low-plastic Ratio model is my favorite for coffee snobs</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Stop the guesswork: Cuisinart’s Clearview air fryer gives you dinner in plain sight ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">Cuisinart Clearview review: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price: </strong>$139/£89<br><strong>Capacity: </strong>4-quart basket<br><strong>Controls:</strong> Touch button<br><strong>Dimensions: </strong>12.2"D x 12.6"W x 11.5"H<br><strong>Modes: </strong>Air Fry, Recrisp, Bake, Roast and Keep Warm<br><strong>Extra features:</strong> Shake reminder, 2 custom presets<br><strong>Noise level: </strong>72 dB (max)<br><strong>Min to Max heat: </strong>212-450°F (100-200°C)<br><strong>Weight: </strong>12.75 pounds</p></div></div><p>Having covered the<a href="https://www.tomsguide.com/home/move-over-ninja-crispi-could-cuisinarts-new-glass-air-fryer-be-the-new-countertop-champion"> Cuisinart’s Clearview air fryer</a> when it was first released in May, I was keen to get my hands on the new release to see whether it’s as smart as it looks. With a clear bowl and open viewing window at the top, it’s no surprise how the company came up with the name. </p><p>However, my first thought wasn’t how well it would cook, but how dirty the clear bowl would get with regular use. If the appliance is kept on your countertop, which is bound to be the most common scenario if you use an air fryer regularly, would it start to look grimy after a few uses?</p><p>So far, it hasn’t been a concern, and I’ve really valued having a clear view of what’s going on inside the bowl without needing to pause to check. </p><p>Here I delve deeper into the Clearview’s performance and give you my verdict on how it stacks up. </p><h3 class="article-body__section" id="section-cuisinart-clearview-review-price-and-availability"><span>Cuisinart Clearview review: Price and availability</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="npnsZCxszp9pemt9VqhM27" name="Cuisinart Clearview 4-quart Air fryer" alt="Cuisinart Clearview 4-quart Air fryer" src="https://cdn.mos.cms.futurecdn.net/npnsZCxszp9pemt9VqhM27.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I tested the Cuisinart Clearview 4-quart air fryer, which costs <a href="https://www.amazon.com/dp/B0GVPBSXXF?" target="_blank" rel="nofollow">$139 at Amazon</a>. You can also pick it up for <a href="https://www.cuisinart.com/clearview-4-quart-glass-basket-air-fryer/AFC-4.html?" target="_blank">$139 direct from Cuisinart</a>. Cuisinart also produces a 9-quart version, but it has a slightly different design, and the cooking drawer only has a clear front. However, despite its larger capacity, it is a mere $20 extra, selling for <a href="https://www.amazon.com/Cuisinart-ClearView-Functions-PFAS-Free-AFC-9/dp/B0GVT7MWY4/ref=sr_1_1?" target="_blank" rel="nofollow">$159 at Amazon.</a></p><p>How does the price compare with the <a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">Ninja Crispi</a>? Although the MSRP is $179, you can currently pick up the Cyberspace Gray and Sage colorway versions of the <a href="https://www.amazon.com/dp/B0DJFB6ZKM?" target="_blank" rel="nofollow">Ninja Crispi for $159 at Amazon.</a> That’s a pretty good deal, since you also get an extra 6-quart bowl included in the box. </p><p>The Clearview is still more expensive than the <a href="https://www.tomsguide.com/home/home-appliances/cosori-turboblaze-air-fryer-review">Cosori 9-in-1 Turboblaze Air Fryer</a>, which is currently discounted to <a href="https://www.amazon.com/TurboBlaze-Premium-Ceramic-Coating-90%C2%B0-450%C2%B0F/dp/B0C33CHG99/ref=sr_1_1?" target="_blank" rel="nofollow">$89 at Amazon</a> and features a 6-quart bowl and plenty more functionality. </p><h3 class="article-body__section" id="section-cuisinart-clearview-review-design"><span>Cuisinart Clearview review: Design</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:58.85%;"><img id="7Ffy6r2peUP8oBde8QAkJc" name="Ninja Crispi vs Cuisinart ClearView air fryers" alt="Ninja Crispi vs Cuisinart ClearView air fryers" src="https://cdn.mos.cms.futurecdn.net/7Ffy6r2peUP8oBde8QAkJc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1177" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Cuisinart/Ninja)</span></figcaption></figure><p>Although I have previously compared <a href="https://www.tomsguide.com/home/move-over-ninja-crispi-could-cuisinarts-new-glass-air-fryer-be-the-new-countertop-champion">Cuisinart’s Clearview with the Ninja Crispi</a>, they're not exact duplicates. Apart from the sloping front on the top, the Clearview takes on a similar form to most air fryers — you slide the basket inside. By comparison, the Crispi's heating pod is placed on top of the cooking bowl. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/fsPEWcPdqjCEDEFneNs4A7.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/N2AF6Y9WYqYJBCMf7dTR97.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZLTmGEzMvjxm7iLRwq8iy6.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure></figure><p><strong>Glass basket</strong><br>The air fryer comes with a square glass bowl with two handles on either side. I found having two side handles, rather than one central handle (like most air fryers), made it easier to manage when sliding and removing it from the base unit.</p><p>A PFAS-free ceramic nonstick crisper plate sits neatly at the bottom of the bowl, but it can be removed if you’re heating something like a pasta bake or using it to store leftovers. </p><p>Of course, if you use your air fryer every day and also tend to store leftovers, you might need to invest in another glass bowl. Although spare bowls aren’t available right now, Cuisinart has told me you'll be able to pick one up in August.</p><h3 class="article-body__section" id="section-controls-and-functionality"><span>Controls and functionality</span></h3><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/fhWfuAYsUgzjuh3KcrciX6.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure></figure><p><strong>Controls and functionality</strong><br>The digital control panel is pretty intuitive to use and easy to access. It doesn’t have as much functionality as other air fryers, but offers air fry, roast, grill, bake, recrisp and keep warm, which is enough for most standard needs.</p><p>However, I did miss the preheating and proofing functions I enjoyed using with the <a href="https://www.tomsguide.com/home/home-appliances/cosori-turboblaze-air-fryer-review">Cosori TurboBlaze air fryer</a>. Although it lets you set two favorites, the temperature doesn’t go below 212º F (100°C), which is too high to proof dough. </p><p><strong>Noise</strong><br>For each cooking mode, I selected the highest temperature setting and monitored the noise output. The top temperature for air fry, roast, grill, and bake was 400ºF (200°C), and 300ºF (150°C) for keep warm.</p><p>The average noise level for air fry and roast was 67 dB (72 dB max), reducing to 64 dB for recrisp, and 56 dB for grill and bake. Keep warm measured an average of 41 dB.<br><br>I don’t think the Cuisinart Clearview air fryer is particularly noisy, and it sits in a similar range to the <a href="https://www.tomsguide.com/home/ninja-air-fryer-pro-in-1-review">Ninja Air Fryer Pro 4-in-1</a>, which takes the top spot in our buying guide.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/pmXGbJzshB957yi9RWMV67.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/W3xDjmfeC6e4s4M49ujhn6.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure></figure><p><strong>Let there be light</strong><br>There is one standout element about this air fryer that really captured my interest and got my husband talking. Having a clear view of what’s cooking cancels out my grumble about the lack of settings. There’s no need to keep pulling out a basket to check how your food is cooking and wasting heat since you can instantly see the progress. </p><p>What’s more, apart from the almost panoramic view of the bowl, you can also view through the top window and illuminate everything. Press the 'light bulb' button, and the internal light will stay on for 45 seconds, unless you remove the basket.</p><p>Apart from the standard food selections I cooked in this air fryer, I also baked cupcakes (I’ll be reporting on this soon), and it was here that the viewing window and clear basket really made a difference.</p><h3 class="article-body__section" id="section-cuisinart-clearview-review-performance"><span>Cuisinart Clearview review: Performance</span></h3><h2 id="meatballs">Meatballs</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/qokrS7xaxps52tsattGp57.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/afWKRTyYNqPfi92DB4g6A7.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure></figure><p>Meatballs are a regular on my family’s meal plan and are easy to cook in an air fryer. I placed 17 meatballs into the glass basket and selected ‘Air Fry’ mode on the control panel. On my U.K. model, it automatically sets to 180°C (350ºF) and 12 mins, but you can adjust the temperature and time by pressing the buttons on the left and right. The temperature can be altered in 5°C increments, and the time by one-minute increments. </p><p>You’ll also notice that the ‘Shake Reminder’ lights up and beeps when the cook is halfway through; if you’d prefer not to use it, just press the button to turn it off.</p><p>I adjusted the time to 10 mins, but kept the temperature at 180°C. I gave the meatballs a shake at five minutes, and they looked quite cooked, so I put them back in and adjusted the time to three minutes. I then checked their internal temperature with my <a href="https://www.tomsguide.com/home/thermoworks-thermapen-one-meat-thermometer-review">Thermoworks meat thermometer </a>to ensure they were cooked through, and then changed the setting to ‘Keep Warm’ until my pasta and sauce were ready. </p><h2 id="croissants">Croissants</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4cNhX2HnVigCat8fbGMC87" name="Cuisinart Clearview 4-quart Air fryer" alt="Cuisinart Clearview 4-quart Air fryer" src="https://cdn.mos.cms.futurecdn.net/4cNhX2HnVigCat8fbGMC87.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>There’s nothing worse than a soft croissant, so I placed three decent-sized croissants in the basket and selected ‘Recrisp’. The automatic setting is 10 minutes at 180°C, but I reset it to three minutes to avoid a charred mess.</p><p>When the three minutes were up, I noticed slight charring on the tops of the croissants, but they were perfectly crispy and even more delicious when I added a generous helping of butter!</p><h2 id="crispy-potatoes-and-fries">Crispy potatoes and fries</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/tuEvC9ASJx59xJSpy9AVt6.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/jxeE6Dtpi8h7Jc3BP9T677.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure></figure><p>Potatoes are one of the regular carbs that I cook in an air fryer. With my first batch, I cut the potatoes into small chunks, leaving the skin intact, then lightly coated them with sunflower oil. I then placed them in the basket and selected ‘Air Fry’, leaving them to cook at the automatic setting — 180°C (350ºF) for 12 minutes.</p><p>At halftime, I gave them a shake and noticed that they still had some time to go; it was only at nine minutes that I saw that they’d started to crisp. After 12 minutes had passed, they were still uncooked, so I increased the temperature to 200°C (400ºF) and cooked them for a further six minutes. At this point, they still lacked the extra crispiness that I prefer, but they were cooked inside.  </p><p>It’s a case of adjusting the setting to suit your preferences. I do also think that preheating the air fryer would help to speed up the cooking.</p><p>I also cooked some frozen fries, straight from the freezer. For these, I followed the cooking results on the food packet and set the air fryer to 200°C for 15 minutes, giving them a shake midway through. The fries came out hot and perfectly crispy.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NHBDTy2htFvhHT3wY32J57" name="Cuisinart Clearview 4-quart Air fryer" alt="Cuisinart Clearview 4-quart Air fryer" src="https://cdn.mos.cms.futurecdn.net/NHBDTy2htFvhHT3wY32J57.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>Having become more accustomed to how the air fryer works, I decided to preheat it to 200°C before cooking some chicken pieces coated in breadcrumbs. And this was exactly what the cooking instructions suggested. I then set the air fryer to ‘Air Fry’ at 180°C for 12 minutes. There was no need to increase the timing, and I could see the benefit of preheating</p><h2 id="roasted-vegetables">Roasted vegetables</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="QsdLWQrScr3McNNPcBPo47" name="Cuisinart Clearview 4-quart Air fryer" alt="Cuisinart Clearview 4-quart Air fryer" src="https://cdn.mos.cms.futurecdn.net/QsdLWQrScr3McNNPcBPo47.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>Just in case you’re worried about my diet and think I only eat carbs and protein, I chopped up some zucchini and peppers. I then gave them a light coating with oil and placed them in the air fryer on ‘Roast’ for 10 minutes at 200°C — the standard setting is at 180°C for 25 minutes. </p><p>I gave them a shake at 5 minutes and, at the 10-minute mark, added an extra two minutes of cook time. They came out slightly charred and soft without being overcooked.</p><h3 class="article-body__section" id="section-cuisinart-clearview-review-verdict"><span>Cuisinart Clearview review: Verdict</span></h3><p>Having eyes on what you’re cooking is the standout feature of this air fryer, and it’s not just a gimmick. It really helps you check on the progress of what you’re cooking without constantly removing the bowl, releasing heat and interrupting whatever else you’re doing in the kitchen. I also loved the light that illuminates your food, and while it’s not intentional, it added a bit of fun to using the air fryer.</p><p>If you have a family, you may find the bowl too small for your needs. If so, you can size up to Cuisinart’s 9-quart ClearView, but the sightline isn’t quite the same. However, overall, it’s a good little air fryer. And despite my early reservations about keeping the bowl clean, it’s been easier to keep spotless than my standard air fryer basket. It may not be as cute as the Ninja Crispi, but it gets the job done.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/stop-the-guesswork-cuisinarts-clearview-air-fryer-gives-you-dinner-in-plain-sight</link>
                                                                            <description>
                            <![CDATA[ Stop opening your air fryer: Why Cuisinart’s Clearview glass basket is a game-changer. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">Yf7ozRMp3syGyKEiLgs5i9</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/XQJ8s4DbdR4anbysxcaqj6-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 17 Jul 2026 11:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Air Fryers]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ camilla.sharman@futurenet.com (Camilla Sharman) ]]></author>                    <dc:creator><![CDATA[ Camilla Sharman ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/nY4nvWzofHKHpvzAqN5LVH.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Camilla is the Homes Staff Writer and covers everything to do with homes and gardens. She has a wealth of editorial experience, mounting over 30 years, and covers news and features, tests products for reviews and compiles buying guides.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Her work has appeared in business and consumer titles, including Ideal Home, Real Homes, House Beautiful, Homebuilding &amp;amp; Renovation, and Kitchen &amp;amp; Bathroom Business. She’s even appeared on the cover of Your Home, writing about her own house renovation.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Although she’s obsessed with decorating her home, she also enjoys baking and trying out the latest kitchen appliances. But when she’s not inside, you’ll find her pottering about in her yard, tending to her vegetable patch or taking in her prized hydrangeas. She also enjoys keeping fit, and if she&#039;s not on a spin bike trying to keep up with the class, she&#039;ll be in the pool, or trying to perfect her headstand in a yoga class.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/XQJ8s4DbdR4anbysxcaqj6-1280-80.jpg">
                                                            <media:credit><![CDATA[Future / Camilla Sharman]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Cuisinart Clearview 4-quart Air fryer ]]></media:description>                                                            <media:text><![CDATA[Cuisinart Clearview 4-quart Air fryer ]]></media:text>
                                <media:title type="plain"><![CDATA[Cuisinart Clearview 4-quart Air fryer ]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/XQJ8s4DbdR4anbysxcaqj6-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">Cuisinart Clearview review: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price: </strong>$139/£89<br><strong>Capacity: </strong>4-quart basket<br><strong>Controls:</strong> Touch button<br><strong>Dimensions: </strong>12.2"D x 12.6"W x 11.5"H<br><strong>Modes: </strong>Air Fry, Recrisp, Bake, Roast and Keep Warm<br><strong>Extra features:</strong> Shake reminder, 2 custom presets<br><strong>Noise level: </strong>72 dB (max)<br><strong>Min to Max heat: </strong>212-450°F (100-200°C)<br><strong>Weight: </strong>12.75 pounds</p></div></div><p>Having covered the<a href="https://www.tomsguide.com/home/move-over-ninja-crispi-could-cuisinarts-new-glass-air-fryer-be-the-new-countertop-champion"> Cuisinart’s Clearview air fryer</a> when it was first released in May, I was keen to get my hands on the new release to see whether it’s as smart as it looks. With a clear bowl and open viewing window at the top, it’s no surprise how the company came up with the name. </p><p>However, my first thought wasn’t how well it would cook, but how dirty the clear bowl would get with regular use. If the appliance is kept on your countertop, which is bound to be the most common scenario if you use an air fryer regularly, would it start to look grimy after a few uses?</p><p>So far, it hasn’t been a concern, and I’ve really valued having a clear view of what’s going on inside the bowl without needing to pause to check. </p><p>Here I delve deeper into the Clearview’s performance and give you my verdict on how it stacks up. </p><h3 class="article-body__section" id="section-cuisinart-clearview-review-price-and-availability"><span>Cuisinart Clearview review: Price and availability</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="npnsZCxszp9pemt9VqhM27" name="Cuisinart Clearview 4-quart Air fryer" alt="Cuisinart Clearview 4-quart Air fryer" src="https://cdn.mos.cms.futurecdn.net/npnsZCxszp9pemt9VqhM27.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>I tested the Cuisinart Clearview 4-quart air fryer, which costs <a href="https://www.amazon.com/dp/B0GVPBSXXF?" target="_blank" rel="nofollow">$139 at Amazon</a>. You can also pick it up for <a href="https://www.cuisinart.com/clearview-4-quart-glass-basket-air-fryer/AFC-4.html?" target="_blank">$139 direct from Cuisinart</a>. Cuisinart also produces a 9-quart version, but it has a slightly different design, and the cooking drawer only has a clear front. However, despite its larger capacity, it is a mere $20 extra, selling for <a href="https://www.amazon.com/Cuisinart-ClearView-Functions-PFAS-Free-AFC-9/dp/B0GVT7MWY4/ref=sr_1_1?" target="_blank" rel="nofollow">$159 at Amazon.</a></p><p>How does the price compare with the <a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">Ninja Crispi</a>? Although the MSRP is $179, you can currently pick up the Cyberspace Gray and Sage colorway versions of the <a href="https://www.amazon.com/dp/B0DJFB6ZKM?" target="_blank" rel="nofollow">Ninja Crispi for $159 at Amazon.</a> That’s a pretty good deal, since you also get an extra 6-quart bowl included in the box. </p><p>The Clearview is still more expensive than the <a href="https://www.tomsguide.com/home/home-appliances/cosori-turboblaze-air-fryer-review">Cosori 9-in-1 Turboblaze Air Fryer</a>, which is currently discounted to <a href="https://www.amazon.com/TurboBlaze-Premium-Ceramic-Coating-90%C2%B0-450%C2%B0F/dp/B0C33CHG99/ref=sr_1_1?" target="_blank" rel="nofollow">$89 at Amazon</a> and features a 6-quart bowl and plenty more functionality. </p><h3 class="article-body__section" id="section-cuisinart-clearview-review-design"><span>Cuisinart Clearview review: Design</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:58.85%;"><img id="7Ffy6r2peUP8oBde8QAkJc" name="Ninja Crispi vs Cuisinart ClearView air fryers" alt="Ninja Crispi vs Cuisinart ClearView air fryers" src="https://cdn.mos.cms.futurecdn.net/7Ffy6r2peUP8oBde8QAkJc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1177" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Cuisinart/Ninja)</span></figcaption></figure><p>Although I have previously compared <a href="https://www.tomsguide.com/home/move-over-ninja-crispi-could-cuisinarts-new-glass-air-fryer-be-the-new-countertop-champion">Cuisinart’s Clearview with the Ninja Crispi</a>, they're not exact duplicates. Apart from the sloping front on the top, the Clearview takes on a similar form to most air fryers — you slide the basket inside. By comparison, the Crispi's heating pod is placed on top of the cooking bowl. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/fsPEWcPdqjCEDEFneNs4A7.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/N2AF6Y9WYqYJBCMf7dTR97.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZLTmGEzMvjxm7iLRwq8iy6.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure></figure><p><strong>Glass basket</strong><br>The air fryer comes with a square glass bowl with two handles on either side. I found having two side handles, rather than one central handle (like most air fryers), made it easier to manage when sliding and removing it from the base unit.</p><p>A PFAS-free ceramic nonstick crisper plate sits neatly at the bottom of the bowl, but it can be removed if you’re heating something like a pasta bake or using it to store leftovers. </p><p>Of course, if you use your air fryer every day and also tend to store leftovers, you might need to invest in another glass bowl. Although spare bowls aren’t available right now, Cuisinart has told me you'll be able to pick one up in August.</p><h3 class="article-body__section" id="section-controls-and-functionality"><span>Controls and functionality</span></h3><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/fhWfuAYsUgzjuh3KcrciX6.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure></figure><p><strong>Controls and functionality</strong><br>The digital control panel is pretty intuitive to use and easy to access. It doesn’t have as much functionality as other air fryers, but offers air fry, roast, grill, bake, recrisp and keep warm, which is enough for most standard needs.</p><p>However, I did miss the preheating and proofing functions I enjoyed using with the <a href="https://www.tomsguide.com/home/home-appliances/cosori-turboblaze-air-fryer-review">Cosori TurboBlaze air fryer</a>. Although it lets you set two favorites, the temperature doesn’t go below 212º F (100°C), which is too high to proof dough. </p><p><strong>Noise</strong><br>For each cooking mode, I selected the highest temperature setting and monitored the noise output. The top temperature for air fry, roast, grill, and bake was 400ºF (200°C), and 300ºF (150°C) for keep warm.</p><p>The average noise level for air fry and roast was 67 dB (72 dB max), reducing to 64 dB for recrisp, and 56 dB for grill and bake. Keep warm measured an average of 41 dB.<br><br>I don’t think the Cuisinart Clearview air fryer is particularly noisy, and it sits in a similar range to the <a href="https://www.tomsguide.com/home/ninja-air-fryer-pro-in-1-review">Ninja Air Fryer Pro 4-in-1</a>, which takes the top spot in our buying guide.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/pmXGbJzshB957yi9RWMV67.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/W3xDjmfeC6e4s4M49ujhn6.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure></figure><p><strong>Let there be light</strong><br>There is one standout element about this air fryer that really captured my interest and got my husband talking. Having a clear view of what’s cooking cancels out my grumble about the lack of settings. There’s no need to keep pulling out a basket to check how your food is cooking and wasting heat since you can instantly see the progress. </p><p>What’s more, apart from the almost panoramic view of the bowl, you can also view through the top window and illuminate everything. Press the 'light bulb' button, and the internal light will stay on for 45 seconds, unless you remove the basket.</p><p>Apart from the standard food selections I cooked in this air fryer, I also baked cupcakes (I’ll be reporting on this soon), and it was here that the viewing window and clear basket really made a difference.</p><h3 class="article-body__section" id="section-cuisinart-clearview-review-performance"><span>Cuisinart Clearview review: Performance</span></h3><h2 id="meatballs">Meatballs</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/qokrS7xaxps52tsattGp57.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/afWKRTyYNqPfi92DB4g6A7.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure></figure><p>Meatballs are a regular on my family’s meal plan and are easy to cook in an air fryer. I placed 17 meatballs into the glass basket and selected ‘Air Fry’ mode on the control panel. On my U.K. model, it automatically sets to 180°C (350ºF) and 12 mins, but you can adjust the temperature and time by pressing the buttons on the left and right. The temperature can be altered in 5°C increments, and the time by one-minute increments. </p><p>You’ll also notice that the ‘Shake Reminder’ lights up and beeps when the cook is halfway through; if you’d prefer not to use it, just press the button to turn it off.</p><p>I adjusted the time to 10 mins, but kept the temperature at 180°C. I gave the meatballs a shake at five minutes, and they looked quite cooked, so I put them back in and adjusted the time to three minutes. I then checked their internal temperature with my <a href="https://www.tomsguide.com/home/thermoworks-thermapen-one-meat-thermometer-review">Thermoworks meat thermometer </a>to ensure they were cooked through, and then changed the setting to ‘Keep Warm’ until my pasta and sauce were ready. </p><h2 id="croissants">Croissants</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4cNhX2HnVigCat8fbGMC87" name="Cuisinart Clearview 4-quart Air fryer" alt="Cuisinart Clearview 4-quart Air fryer" src="https://cdn.mos.cms.futurecdn.net/4cNhX2HnVigCat8fbGMC87.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>There’s nothing worse than a soft croissant, so I placed three decent-sized croissants in the basket and selected ‘Recrisp’. The automatic setting is 10 minutes at 180°C, but I reset it to three minutes to avoid a charred mess.</p><p>When the three minutes were up, I noticed slight charring on the tops of the croissants, but they were perfectly crispy and even more delicious when I added a generous helping of butter!</p><h2 id="crispy-potatoes-and-fries">Crispy potatoes and fries</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/tuEvC9ASJx59xJSpy9AVt6.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/jxeE6Dtpi8h7Jc3BP9T677.jpg" alt="Cuisinart Clearview 4-quart Air fryer " /><figcaption><small role="credit">Future / Camilla Sharman</small></figcaption></figure></figure><p>Potatoes are one of the regular carbs that I cook in an air fryer. With my first batch, I cut the potatoes into small chunks, leaving the skin intact, then lightly coated them with sunflower oil. I then placed them in the basket and selected ‘Air Fry’, leaving them to cook at the automatic setting — 180°C (350ºF) for 12 minutes.</p><p>At halftime, I gave them a shake and noticed that they still had some time to go; it was only at nine minutes that I saw that they’d started to crisp. After 12 minutes had passed, they were still uncooked, so I increased the temperature to 200°C (400ºF) and cooked them for a further six minutes. At this point, they still lacked the extra crispiness that I prefer, but they were cooked inside.  </p><p>It’s a case of adjusting the setting to suit your preferences. I do also think that preheating the air fryer would help to speed up the cooking.</p><p>I also cooked some frozen fries, straight from the freezer. For these, I followed the cooking results on the food packet and set the air fryer to 200°C for 15 minutes, giving them a shake midway through. The fries came out hot and perfectly crispy.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NHBDTy2htFvhHT3wY32J57" name="Cuisinart Clearview 4-quart Air fryer" alt="Cuisinart Clearview 4-quart Air fryer" src="https://cdn.mos.cms.futurecdn.net/NHBDTy2htFvhHT3wY32J57.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>Having become more accustomed to how the air fryer works, I decided to preheat it to 200°C before cooking some chicken pieces coated in breadcrumbs. And this was exactly what the cooking instructions suggested. I then set the air fryer to ‘Air Fry’ at 180°C for 12 minutes. There was no need to increase the timing, and I could see the benefit of preheating</p><h2 id="roasted-vegetables">Roasted vegetables</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="QsdLWQrScr3McNNPcBPo47" name="Cuisinart Clearview 4-quart Air fryer" alt="Cuisinart Clearview 4-quart Air fryer" src="https://cdn.mos.cms.futurecdn.net/QsdLWQrScr3McNNPcBPo47.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>Just in case you’re worried about my diet and think I only eat carbs and protein, I chopped up some zucchini and peppers. I then gave them a light coating with oil and placed them in the air fryer on ‘Roast’ for 10 minutes at 200°C — the standard setting is at 180°C for 25 minutes. </p><p>I gave them a shake at 5 minutes and, at the 10-minute mark, added an extra two minutes of cook time. They came out slightly charred and soft without being overcooked.</p><h3 class="article-body__section" id="section-cuisinart-clearview-review-verdict"><span>Cuisinart Clearview review: Verdict</span></h3><p>Having eyes on what you’re cooking is the standout feature of this air fryer, and it’s not just a gimmick. It really helps you check on the progress of what you’re cooking without constantly removing the bowl, releasing heat and interrupting whatever else you’re doing in the kitchen. I also loved the light that illuminates your food, and while it’s not intentional, it added a bit of fun to using the air fryer.</p><p>If you have a family, you may find the bowl too small for your needs. If so, you can size up to Cuisinart’s 9-quart ClearView, but the sightline isn’t quite the same. However, overall, it’s a good little air fryer. And despite my early reservations about keeping the bowl clean, it’s been easier to keep spotless than my standard air fryer basket. It may not be as cute as the Ninja Crispi, but it gets the job done.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I banned indoor cooking this heatwave — and this Weber grill saved my sanity ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Sometimes the best things in life are the simplest. Black coffee. A good cast iron. A classic kettle BBQ — maybe, say, the Weber Master-Touch. Do you need any more?</p><p>Where I live, in the U.K., with our tiny gardens, small houses and very reserved cuts of meat (the spirit of rationing lives on), at least compared to the States, the classic kettle is <em>the</em> default grill. Obviously, in the land of eagles, pick-up trucks and Traegers, everything is a little larger: bigger gardens, houses and, of course, grills. On the ‘freedom’ scale, the Weber Master-Touch might look like a “my first BBQ”.</p><p>But what if you only have a small amount of space and need a compact BBQ? Could Weber’s latest kettle, with its reasonable price and fairly small cooking surface, still be the <a href="https://www.tomsguide.com/best-picks/best-grills">best grill</a> for you? I think so. Keep reading my full Weber Master-Touch review to find out more.</p><h2 class="article-body__section" id="section-weber-master-touch-review-specs"><span>Weber Master-Touch review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p>Starts at <a href="https://www.weber.com/US/en/charcoal/master-touch/master-touch-charcoal-grill-22/14501001.html" target="_blank" rel="nofollow">$289</a> / <a href="https://www.weber.com/GB/en/charcoal-barbecues/master-touch/master-touch-gbs-e-5750-charcoal-barbecue-57-cm/14701004.html" target="_blank" rel="nofollow">£289</a> (22-inch); <a href="https://www.weber.com/US/en/charcoal/original-kettle/master-touch-26-charcoal-grill/1500064.html" target="_blank" rel="nofollow">$499</a> / <a href="https://www.weber.com/GB/en/charcoal-barbecues/master-touch/1500230.html" target="_blank" rel="nofollow">£475</a> (26-inch)</p></td></tr><tr><td class="firstcol " ><p><strong>Type</strong></p></td><td  ><p>Kettle grill</p></td></tr><tr><td class="firstcol " ><p><strong>Diameter</strong></p></td><td  ><p>22 inches; 26 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Cooking area</strong></p></td><td  ><p>508 sq inches</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>42.9 x 27 x 33 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Fuel</strong></p></td><td  ><p>Charcoal</p></td></tr><tr><td class="firstcol " ><p><strong>App</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black; slate blue; ocean blue; spring green; smoke gray; ivory; burnt orange.</p></td></tr><tr><td class="firstcol " ><p><strong>Warranty (default)</strong></p></td><td  ><p>Bowl & lid — 10 years; One touch cleaning system & plastic components — 5 years; everything else — 2 years</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-weber-master-touch-review-price-availability"><span>Weber Master-Touch review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="7t8b4T3oSSymDdMnZGxJhN" name="Weber Master-Touch-6" alt="The Weber Master-Touch on a lawn" src="https://cdn.mos.cms.futurecdn.net/7t8b4T3oSSymDdMnZGxJhN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Weber Master-Touch is a fairly affordable grill, starting at <a href="https://www.weber.com/US/en/charcoal/master-touch/master-touch-charcoal-grill-22/14501001.html" target="_blank" rel="nofollow">$289</a> / <a href="https://www.weber.com/GB/en/charcoal-barbecues/master-touch/master-touch-gbs-e-5750-charcoal-barbecue-57-cm/14701004.html" target="_blank" rel="nofollow">£289</a> for the base 22-inch (57cm) model. The larger 26-inch (67cm) model, meanwhile, starts at <a href="https://www.weber.com/US/en/charcoal/original-kettle/master-touch-26-charcoal-grill/1500064.html" target="_blank" rel="nofollow">$499</a> / <a href="https://www.weber.com/GB/en/charcoal-barbecues/master-touch/1500230.html" target="_blank" rel="nofollow">£475</a>. So, if you can’t afford to splurge $2,000 on our favorite charcoal grill, the <a href="https://www.tomsguide.com/home/outdoors/kamado-joe-konnected--joe-grill-review">Kamado Joe</a> kettle, the Master Touch will still give you the bare essentials at a fraction of the cost.</p><p>You do literally get the “bare essentials” though. There’s no app, no electricity, no automated temperature control, no temperature food probes included. It’s simple and honest, and I find that kinda fun. I actually prefer using the Master-Touch to my tricked-out <a href="https://www.tomsguide.com/home/outdoors/weber-searwood-xl-600-review">Weber Searwood XL 600</a> ($1,199) precisely <em>because</em> I have to do everything myself — plan heat zones, keep tabs on temperature, and carefully manage grill space. It feels like actual cooking, not just waiting for something to do the cooking for you. Still, if you <em>do</em> want more features, the <a href="https://www.tomsguide.com/home/outdoors/masterbuilt-autoignite-series-545-digital-charcoal-grill-and-smoker-review">Masterbuilt AutoIgnite Series 545</a> is only a dollar more expensive than the 26-inch Master-Touch, at $500.</p><h4 id="customization">Customization</h4><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="Kq4ewQ8RKQByGrpAELvTZP" name="Weber Master-Touch-12" alt="The Weber Master-Touch on a lawn with the grills on show" src="https://cdn.mos.cms.futurecdn.net/Kq4ewQ8RKQByGrpAELvTZP.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>A neat feature of the Master-Touch is the ability to customize it at checkout. You can choose from a range of different colors, including slate blue (which I chose) and a gorgeous burnt orange, among others. </p><p>You can then customize the color of the ash tray (or “One-Touch” cleaning system, to give its glorified name), which is a waste of money if you ask me; add premium stainless steel grates with a 5-year warranty (I tested these); and bolt on some accessories like a low-n-slow kit, accessory hangers, chimney starters, and more. I like that you can make the Master-Touch your own, especially as Weber kettles are so common where I live. It’s not something we see from other brands, like Kamado Joe or Traeger (although, in fairness, those grills come loaded with everything in the first place).</p><h2 class="article-body__section" id="section-weber-master-touch-review-fuel"><span>Weber Master-Touch review: Fuel</span></h2><p>The Master-Touch is a charcoal grill, so fueling is pretty simple. Load up some briquettes or lumpwood charcoal according to your desired heat zones, fire up, and 20-minutes-or-so later you’re cookin’. I’ve also been adding small wood logs for extra smokiness. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="SRWqYGouaL2qF8RDvFFggP" name="Weber Master-Touch-11" alt="The Weber Master-Touch with the central grill removed" src="https://cdn.mos.cms.futurecdn.net/SRWqYGouaL2qF8RDvFFggP.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>While it lacks the immediacy of gas/electric or the fancy-pants-ness of pellets, charcoal is much cheaper than both. But it has one other key advantage, especially here in the U.K. — it’s incredibly easy to source. If you run out of fancier fuels, it can be tough to get more at short notice. I’ve run out of pellets mid-cook before on long smokes, leading to a nervous wait for an Amazon next-day delivery. And, recently, I had to do a frantic dash around several local gas stations to get propane for an upcoming pizza oven review (there are often summertime propane shortages where I live). </p><p>Charcoal, meanwhile, can be bought from every gas station, large grocery store or garden center here in Britain. That means, if the sun comes out suddenly and you find yourself without fuel, you can quickly grab some from down the street for an impromptu BBQ.</p><h2 class="article-body__section" id="section-weber-master-touch-review-design-features"><span>Weber Master-Touch review: Design & features</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="XXcjnMy2YMSsHspAe5YtFP" name="Weber Master-Touch-8" alt="The Weber Master-Touch on a lawn with the lid open" src="https://cdn.mos.cms.futurecdn.net/XXcjnMy2YMSsHspAe5YtFP.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Weber Master-Touch uses the classic round kettle design. I tested the 22-inch / 57cm barbecue, which is fairly compact versus larger, rectangular grills and smokers. This means it’ll fit on a moderately-sized balcony or in a small yard. I was able to position the Master-Touch in a small space between my lawn and shed, where it fit perfectly and didn’t overload our already tech-filled patio. 22 inches gives you easily enough space for at least a family of four, although I’d buy the larger 26-inch model if you’re planning on bigger cooks.</p><p>The main kettle lid features a slide-back rail, allowing you to flip the lid 90 degrees while cooking, to turn food or let off some heat if things are too hot. In the lid is an old-school analog thermometer giving you ambient temperature readings, although for more accurate measurements (especially when smoking or grilling large cuts of meat) you’ll want to invest in food probes. The upper handle features a heat shield, which is a nice safety feature, while the front handle has inlaid notches for hanging accessories — very useful.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="PDD6pqvGtDK938gU2GLn4N" name="Weber Master-Touch-14" alt="The Weber Master-Touch One-Touch cleaning system" src="https://cdn.mos.cms.futurecdn.net/PDD6pqvGtDK938gU2GLn4N.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Under the main kettle sits an ashtray, which employs a sliding vent system. This is called a “One-Touch” cleaning system, although it’s far from single-touch. The vent has two benefits: it’s part of the ventilation system for controlling your heat and flames, but also helps sweep fallen ash into the tray below. Under that is a storage shelf that doubles as a brace for the legs and wheels — I put my weatherproof Weber storage box under there, where I keep my probes, cooking utensils and heatproof gloves.</p><p>Build quality is mostly pretty good. The kettle itself, handles and trim, grates, and ashtray all use premium metal and black plastics, which feels high-quality and sturdy. The legs are made from lightweight aluminum, but they don’t feel weak at all. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="achbvwJ7ZZsfgKJX44tWhM" name="Weber Master-Touch-13" alt="The Weber Master-Touch wheel" src="https://cdn.mos.cms.futurecdn.net/achbvwJ7ZZsfgKJX44tWhM.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The only things letting the Master-Touch down are the wheels. They’re super light and cheap-feeling, and don’t attach as securely to the legs as I’d like. They shake and bend a lot when wheeling the grill, meaning the Master-Touch is actually quite difficult to move over anything but perfectly smooth terrain. I get this grill is in Weber’s budget range, but the wheels still feel way too cheap for me — like something from a crappy $30 grocery store grill.</p><h2 id="assembly">Assembly</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/KbBQsTkoSfR3H7bnVtR36E.jpg" alt="The Weber Master-Touch being assembled" /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/F8uAekL2A2TyqRU7kdfGqE.jpg" alt="The Weber Master-Touch being assembled" /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/rveu6DeKbgUTxB9KzXTFwE.jpg" alt="The Weber Master-Touch being assembled" /><figcaption><small role="credit">Future</small></figcaption></figure></figure><p>You’ll need to assemble your Weber Master-Touch once it arrives. Mine arrived well packaged — the grill was free of marks or damage even though the box had been dented and squashed by the courier.</p><p>The assembly took me around 45 minutes, although I was going slowly as I was constructing outside during a heatwave here in the U.K., in 102°F / 38°C. Everything was clearly labelled and the instructions were very easy to follow. There’s a plastic hex key included, which will essentially cover your whole build. It’s a bit slow, so I got out my motorcycle tool kit and used a ratchet. Weber advises against using power tools, as these make it very easy to overtighten and ruin nuts/bolts.</p><h2 class="article-body__section" id="section-weber-master-touch-review-controls"><span>Weber Master-Touch review: Controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="WUZXLxLzP52VCzoZap6RMN" name="Weber Master-Touch-16" alt="The Weber Master-Touch vent being adjusted by hand" src="https://cdn.mos.cms.futurecdn.net/WUZXLxLzP52VCzoZap6RMN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Master-Touch is a basic BBQ with equally basic controls, so it’ll suit newcomers to grilling. You’ve got a vent on top and a vent at the base, which you use to control airflow and heat. It’ll take a bit of trial and error to figure out how to use these for consistent heat, and a lot of that will be down to fueling, but that’s all part of the fun. As I mentioned above, there’s an ambient temperature gauge on top which is extremely useful for regulating heat. The temperature dial is very responsive too, and moves quickly as the internal heat changes.</p><p>Unlike other Weber grills, like my Searwood XL 600, the Master-Touch has no electrical components and therefore no wireless connectivity and heat control. This is the one thing I really missed. I <em>love</em> controlling my Searwood from inside the house or, yes, the pub, using Weber’s fantastic companion app. But the Master-Touch simply isn’t that kind of grill.</p><h2 class="article-body__section" id="section-weber-master-touch-review-performance"><span>Weber Master-Touch review: Performance</span></h2><p>The Master-Touch is a kettle grill, and it performs like, well, pretty much every other kettle grill. It’ll happily get up to 750F/400C or higher if you fuel it accordingly, making it a monster for those quick, powerful sears.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1831px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="23iHyrQx3Cag5R7RoEiLdP" name="Weber Master-Touch-18" alt="Food being cooked on the Weber Master-Touch" src="https://cdn.mos.cms.futurecdn.net/v2/t:41,l:0,cw:1831,ch:1030,q:80/23iHyrQx3Cag5R7RoEiLdP.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>If you’re just cooking on high heat, it’s a very straightforward and competent grill. The 22-inch cooking surface was enough space for me to simultaneously cook around 2lbs of chicken wings, several slices of pork belly, two burgers, two veggie skewers and two corns. Admittedly, space was a little tight — I had 4.5lbs of chicken in total, which had to be done in two batches — but I managed. </p><p>I arranged the fuel half-and-half for direct/indirect heat underneath the cooking grill. I had direct heat on one half at around 480-525F / 250-275C, with a lower indirect heat on the other half to slowly cook through the riskier meats. The fuel grate is positioned at the ideal level below the main grate — you only need a single layer of charcoal to get to 480F / 250C and higher under the cooking grate, enough for strong sears.</p><p>22 inches is (just) enough cooking space to let you get creative with your charcoal layout and create different heating zones. This is especially important for smoking, where you can use various layouts (like the charcoal snake method) to channel the fuel burn along a route, creating lower heat and smoke for longer. If you’re going to be smoking more, the 26-inch model will give you more space for your layouts, and is what I’d recommend. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="QbjAt8rqan3VPhPP4GTHSN" name="Weber Master-Touch-15" alt="The Weber Master-Touch thermometer" src="https://cdn.mos.cms.futurecdn.net/QbjAt8rqan3VPhPP4GTHSN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The ambient temperature dial on the top of the kettle is also a great help for smoking, letting you keep an eye on temperature, although again, you’ll want meat probes to smoke accurately. </p><p>For any serious smoking, though, I would advise investing in a dedicated smoker like the Searwood XL 600, Kamado Joe, or <a href="https://www.tomsguide.com/home/outdoors/traeger-woodridge-pro-grill-review">Traegar Woodridge Pro</a> ($999). Although pricey, these have features to make smoking much more accurate and consistent, while giving more control over taste — I’m talking real-time handling of fuel burn, smoke levels and temperature, etc. They also have apps, so you can control everything from afar. However, if, like me, you only smoke food occasionally, but mostly use a grill for searing, the Master-Touch is more than adequate.</p><h2 class="article-body__section" id="section-weber-master-touch-review-storage-maintenance"><span>Weber Master-Touch review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="fCMg4PZ438Pf5sMMMaQKrN" name="Weber Master-Touch-10" alt="The Weber Master-Touch One-Touch cleaning system" src="https://cdn.mos.cms.futurecdn.net/fCMg4PZ438Pf5sMMMaQKrN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>As I mentioned earlier, as far as grills go, the Master-Touch is fairly compact, making it easy to stash away in a neat spot on your patio, or under cover in your shed or garage. Weber gear is designed to withstand being kept outdoors, and indeed my Searwood has lived outside, uncovered for around a year, with only a modicum of rust showing up. Ideally, though, you’d want to store the Master-Touch (or any grill) under a cover to shield it from the elements.</p><p>The Master-Touch benefits from Weber’s generous warranty: 10 years on the bowl and lid; 5 years on the ash tray system and plastic components; and 2 years on everything else. Opt for the upgraded stainless grates, though, and those come with a 5-year warranty.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="sUPfXdJmoyLQUh8AVNFVJD" name="IMG_1932" alt="The Weber Master-Touch with the One-Touch cleaning system being used by hand" src="https://cdn.mos.cms.futurecdn.net/sUPfXdJmoyLQUh8AVNFVJD.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Maintenance is easy — another benefit of small grills. Clear the fuel out, dump the ash using the “One-Touch” system (which you’ll need to slide back and forth… around 10-15 times), then remove and clean the grills with BBQ cleaner, before repeating on the kettle and lid. I use Weber’s wire brush to remove large debris from the grill, then give it a good spray and wipe. For tougher-to-shift muck, I’ve put the grills in my dishwasher, which they can stand up to due to being stainless, but will discolor them over time (I don’t care, though, as they get blackened during cooking anyway). It took me around 10-15 minutes for a quick clean when I knew I was cooking again the next day, and around 30 minutes for a deeper clean.</p><p>Listen, I’m not moaning, because this was an easy grill to clean. But as you can see, to call it “One-Touch” is incredibly ambitious, to say the least.</p><h2 class="article-body__section" id="section-weber-master-touch-review-verdict"><span>Weber Master-Touch review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="J7bR2uE8njqzifDzyUwwxN" name="Weber Master-Touch-7" alt="The Weber Master-Touch on a lawn" src="https://cdn.mos.cms.futurecdn.net/J7bR2uE8njqzifDzyUwwxN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Like I said earlier, there’s value in simplicity. The Weber Master-Touch is a simple kettle grill with a few quality-of-life features. That means it’s not going to wow you with modern features, but it also makes it difficult to go wrong.</p><p>The Master-Touch cooks great, is customizable, and easy to both store and maintain. It gives you just enough space to get creative with fueling, allowing different temperature zones and smoking. And it gets super hot to put beautiful sears on food. Sure, the wheels feel a little flimsy, but the rest of the grill is built extremely well, and besides, the Master-Touch <em>is</em> an affordable grill. </p><p>I've been using the Master-Touch all summer here in the U.K., where we've had a heatwave (reaching up to 102F). Cooking inside in such conditions is awful (we don't have AC in our homes here), and I've really appreciate having the Master-Touch — its small size, quick heat up and ease-of-cleaning has let me cook al-fresco quickly and easily.</p><p>If it’s a compact, affordable but quality grilling solution you’re after for this summer, the Weber Master-Touch comes highly recommended by me.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/weber-master-touch-review</link>
                                                                            <description>
                            <![CDATA[ Simple but effective, the Weber Master-Touch is a modest spin on the classic kettle grill. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">AG5qoqwdWbkeEw3w5B9wg7</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/E2byrVFVWRWaQsHuFk6r5m-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 17 Jul 2026 08:41:55 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ peter.wolinski@futurenet.com (Peter Wolinski) ]]></author>                    <dc:creator><![CDATA[ Peter Wolinski ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/stgPfXWY7ukw8J8rfC7vjg.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Peter is a Senior Editor at Tom&#039;s Guide, heading up the site&#039;s Reviews team and Cameras section. Having built gaming PCs since he was 10 (that&#039;s a while ago now) he&#039;s a bit of a nerd about components and hardware. He&#039;s also been an iPhone user since the classic iPhone 4, and a Mac user for well over a decade. Experienced in using and testing all kinds of technology — from phones through to tablets, computers, games consoles, cameras and smart home tech — helping people find the best tech for them (at the best prices) is what Peter does best. A photographer since he bought his first camera (a Fujifilm) in 2015, Peter was previously an Editor for Canon-Europe.com. He then edited the Cameras and How To sections of Tom&#039;s Guide. When he&#039;s not crafting helpful, in-depth reviews, Peter can usually be found out and about honing his architectural photography skills, riding his motorcycle around Welsh mountain roads, telling everyone about his two greyhounds, squeezing a few extra FPS out of PC games or perfecting his espresso shots.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/E2byrVFVWRWaQsHuFk6r5m-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A split image with the Weber Master-Touch on the left and grilled food on the right, being cooked on the Master-Touch]]></media:description>                                                            <media:text><![CDATA[A split image with the Weber Master-Touch on the left and grilled food on the right, being cooked on the Master-Touch]]></media:text>
                                <media:title type="plain"><![CDATA[A split image with the Weber Master-Touch on the left and grilled food on the right, being cooked on the Master-Touch]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/E2byrVFVWRWaQsHuFk6r5m-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Sometimes the best things in life are the simplest. Black coffee. A good cast iron. A classic kettle BBQ — maybe, say, the Weber Master-Touch. Do you need any more?</p><p>Where I live, in the U.K., with our tiny gardens, small houses and very reserved cuts of meat (the spirit of rationing lives on), at least compared to the States, the classic kettle is <em>the</em> default grill. Obviously, in the land of eagles, pick-up trucks and Traegers, everything is a little larger: bigger gardens, houses and, of course, grills. On the ‘freedom’ scale, the Weber Master-Touch might look like a “my first BBQ”.</p><p>But what if you only have a small amount of space and need a compact BBQ? Could Weber’s latest kettle, with its reasonable price and fairly small cooking surface, still be the <a href="https://www.tomsguide.com/best-picks/best-grills">best grill</a> for you? I think so. Keep reading my full Weber Master-Touch review to find out more.</p><h2 class="article-body__section" id="section-weber-master-touch-review-specs"><span>Weber Master-Touch review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p>Starts at <a href="https://www.weber.com/US/en/charcoal/master-touch/master-touch-charcoal-grill-22/14501001.html" target="_blank" rel="nofollow">$289</a> / <a href="https://www.weber.com/GB/en/charcoal-barbecues/master-touch/master-touch-gbs-e-5750-charcoal-barbecue-57-cm/14701004.html" target="_blank" rel="nofollow">£289</a> (22-inch); <a href="https://www.weber.com/US/en/charcoal/original-kettle/master-touch-26-charcoal-grill/1500064.html" target="_blank" rel="nofollow">$499</a> / <a href="https://www.weber.com/GB/en/charcoal-barbecues/master-touch/1500230.html" target="_blank" rel="nofollow">£475</a> (26-inch)</p></td></tr><tr><td class="firstcol " ><p><strong>Type</strong></p></td><td  ><p>Kettle grill</p></td></tr><tr><td class="firstcol " ><p><strong>Diameter</strong></p></td><td  ><p>22 inches; 26 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Cooking area</strong></p></td><td  ><p>508 sq inches</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>42.9 x 27 x 33 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Fuel</strong></p></td><td  ><p>Charcoal</p></td></tr><tr><td class="firstcol " ><p><strong>App</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black; slate blue; ocean blue; spring green; smoke gray; ivory; burnt orange.</p></td></tr><tr><td class="firstcol " ><p><strong>Warranty (default)</strong></p></td><td  ><p>Bowl & lid — 10 years; One touch cleaning system & plastic components — 5 years; everything else — 2 years</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-weber-master-touch-review-price-availability"><span>Weber Master-Touch review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="7t8b4T3oSSymDdMnZGxJhN" name="Weber Master-Touch-6" alt="The Weber Master-Touch on a lawn" src="https://cdn.mos.cms.futurecdn.net/7t8b4T3oSSymDdMnZGxJhN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Weber Master-Touch is a fairly affordable grill, starting at <a href="https://www.weber.com/US/en/charcoal/master-touch/master-touch-charcoal-grill-22/14501001.html" target="_blank" rel="nofollow">$289</a> / <a href="https://www.weber.com/GB/en/charcoal-barbecues/master-touch/master-touch-gbs-e-5750-charcoal-barbecue-57-cm/14701004.html" target="_blank" rel="nofollow">£289</a> for the base 22-inch (57cm) model. The larger 26-inch (67cm) model, meanwhile, starts at <a href="https://www.weber.com/US/en/charcoal/original-kettle/master-touch-26-charcoal-grill/1500064.html" target="_blank" rel="nofollow">$499</a> / <a href="https://www.weber.com/GB/en/charcoal-barbecues/master-touch/1500230.html" target="_blank" rel="nofollow">£475</a>. So, if you can’t afford to splurge $2,000 on our favorite charcoal grill, the <a href="https://www.tomsguide.com/home/outdoors/kamado-joe-konnected--joe-grill-review">Kamado Joe</a> kettle, the Master Touch will still give you the bare essentials at a fraction of the cost.</p><p>You do literally get the “bare essentials” though. There’s no app, no electricity, no automated temperature control, no temperature food probes included. It’s simple and honest, and I find that kinda fun. I actually prefer using the Master-Touch to my tricked-out <a href="https://www.tomsguide.com/home/outdoors/weber-searwood-xl-600-review">Weber Searwood XL 600</a> ($1,199) precisely <em>because</em> I have to do everything myself — plan heat zones, keep tabs on temperature, and carefully manage grill space. It feels like actual cooking, not just waiting for something to do the cooking for you. Still, if you <em>do</em> want more features, the <a href="https://www.tomsguide.com/home/outdoors/masterbuilt-autoignite-series-545-digital-charcoal-grill-and-smoker-review">Masterbuilt AutoIgnite Series 545</a> is only a dollar more expensive than the 26-inch Master-Touch, at $500.</p><h4 id="customization">Customization</h4><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="Kq4ewQ8RKQByGrpAELvTZP" name="Weber Master-Touch-12" alt="The Weber Master-Touch on a lawn with the grills on show" src="https://cdn.mos.cms.futurecdn.net/Kq4ewQ8RKQByGrpAELvTZP.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>A neat feature of the Master-Touch is the ability to customize it at checkout. You can choose from a range of different colors, including slate blue (which I chose) and a gorgeous burnt orange, among others. </p><p>You can then customize the color of the ash tray (or “One-Touch” cleaning system, to give its glorified name), which is a waste of money if you ask me; add premium stainless steel grates with a 5-year warranty (I tested these); and bolt on some accessories like a low-n-slow kit, accessory hangers, chimney starters, and more. I like that you can make the Master-Touch your own, especially as Weber kettles are so common where I live. It’s not something we see from other brands, like Kamado Joe or Traeger (although, in fairness, those grills come loaded with everything in the first place).</p><h2 class="article-body__section" id="section-weber-master-touch-review-fuel"><span>Weber Master-Touch review: Fuel</span></h2><p>The Master-Touch is a charcoal grill, so fueling is pretty simple. Load up some briquettes or lumpwood charcoal according to your desired heat zones, fire up, and 20-minutes-or-so later you’re cookin’. I’ve also been adding small wood logs for extra smokiness. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="SRWqYGouaL2qF8RDvFFggP" name="Weber Master-Touch-11" alt="The Weber Master-Touch with the central grill removed" src="https://cdn.mos.cms.futurecdn.net/SRWqYGouaL2qF8RDvFFggP.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>While it lacks the immediacy of gas/electric or the fancy-pants-ness of pellets, charcoal is much cheaper than both. But it has one other key advantage, especially here in the U.K. — it’s incredibly easy to source. If you run out of fancier fuels, it can be tough to get more at short notice. I’ve run out of pellets mid-cook before on long smokes, leading to a nervous wait for an Amazon next-day delivery. And, recently, I had to do a frantic dash around several local gas stations to get propane for an upcoming pizza oven review (there are often summertime propane shortages where I live). </p><p>Charcoal, meanwhile, can be bought from every gas station, large grocery store or garden center here in Britain. That means, if the sun comes out suddenly and you find yourself without fuel, you can quickly grab some from down the street for an impromptu BBQ.</p><h2 class="article-body__section" id="section-weber-master-touch-review-design-features"><span>Weber Master-Touch review: Design & features</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="XXcjnMy2YMSsHspAe5YtFP" name="Weber Master-Touch-8" alt="The Weber Master-Touch on a lawn with the lid open" src="https://cdn.mos.cms.futurecdn.net/XXcjnMy2YMSsHspAe5YtFP.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Weber Master-Touch uses the classic round kettle design. I tested the 22-inch / 57cm barbecue, which is fairly compact versus larger, rectangular grills and smokers. This means it’ll fit on a moderately-sized balcony or in a small yard. I was able to position the Master-Touch in a small space between my lawn and shed, where it fit perfectly and didn’t overload our already tech-filled patio. 22 inches gives you easily enough space for at least a family of four, although I’d buy the larger 26-inch model if you’re planning on bigger cooks.</p><p>The main kettle lid features a slide-back rail, allowing you to flip the lid 90 degrees while cooking, to turn food or let off some heat if things are too hot. In the lid is an old-school analog thermometer giving you ambient temperature readings, although for more accurate measurements (especially when smoking or grilling large cuts of meat) you’ll want to invest in food probes. The upper handle features a heat shield, which is a nice safety feature, while the front handle has inlaid notches for hanging accessories — very useful.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="PDD6pqvGtDK938gU2GLn4N" name="Weber Master-Touch-14" alt="The Weber Master-Touch One-Touch cleaning system" src="https://cdn.mos.cms.futurecdn.net/PDD6pqvGtDK938gU2GLn4N.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Under the main kettle sits an ashtray, which employs a sliding vent system. This is called a “One-Touch” cleaning system, although it’s far from single-touch. The vent has two benefits: it’s part of the ventilation system for controlling your heat and flames, but also helps sweep fallen ash into the tray below. Under that is a storage shelf that doubles as a brace for the legs and wheels — I put my weatherproof Weber storage box under there, where I keep my probes, cooking utensils and heatproof gloves.</p><p>Build quality is mostly pretty good. The kettle itself, handles and trim, grates, and ashtray all use premium metal and black plastics, which feels high-quality and sturdy. The legs are made from lightweight aluminum, but they don’t feel weak at all. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="achbvwJ7ZZsfgKJX44tWhM" name="Weber Master-Touch-13" alt="The Weber Master-Touch wheel" src="https://cdn.mos.cms.futurecdn.net/achbvwJ7ZZsfgKJX44tWhM.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The only things letting the Master-Touch down are the wheels. They’re super light and cheap-feeling, and don’t attach as securely to the legs as I’d like. They shake and bend a lot when wheeling the grill, meaning the Master-Touch is actually quite difficult to move over anything but perfectly smooth terrain. I get this grill is in Weber’s budget range, but the wheels still feel way too cheap for me — like something from a crappy $30 grocery store grill.</p><h2 id="assembly">Assembly</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/KbBQsTkoSfR3H7bnVtR36E.jpg" alt="The Weber Master-Touch being assembled" /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/F8uAekL2A2TyqRU7kdfGqE.jpg" alt="The Weber Master-Touch being assembled" /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/rveu6DeKbgUTxB9KzXTFwE.jpg" alt="The Weber Master-Touch being assembled" /><figcaption><small role="credit">Future</small></figcaption></figure></figure><p>You’ll need to assemble your Weber Master-Touch once it arrives. Mine arrived well packaged — the grill was free of marks or damage even though the box had been dented and squashed by the courier.</p><p>The assembly took me around 45 minutes, although I was going slowly as I was constructing outside during a heatwave here in the U.K., in 102°F / 38°C. Everything was clearly labelled and the instructions were very easy to follow. There’s a plastic hex key included, which will essentially cover your whole build. It’s a bit slow, so I got out my motorcycle tool kit and used a ratchet. Weber advises against using power tools, as these make it very easy to overtighten and ruin nuts/bolts.</p><h2 class="article-body__section" id="section-weber-master-touch-review-controls"><span>Weber Master-Touch review: Controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="WUZXLxLzP52VCzoZap6RMN" name="Weber Master-Touch-16" alt="The Weber Master-Touch vent being adjusted by hand" src="https://cdn.mos.cms.futurecdn.net/WUZXLxLzP52VCzoZap6RMN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Master-Touch is a basic BBQ with equally basic controls, so it’ll suit newcomers to grilling. You’ve got a vent on top and a vent at the base, which you use to control airflow and heat. It’ll take a bit of trial and error to figure out how to use these for consistent heat, and a lot of that will be down to fueling, but that’s all part of the fun. As I mentioned above, there’s an ambient temperature gauge on top which is extremely useful for regulating heat. The temperature dial is very responsive too, and moves quickly as the internal heat changes.</p><p>Unlike other Weber grills, like my Searwood XL 600, the Master-Touch has no electrical components and therefore no wireless connectivity and heat control. This is the one thing I really missed. I <em>love</em> controlling my Searwood from inside the house or, yes, the pub, using Weber’s fantastic companion app. But the Master-Touch simply isn’t that kind of grill.</p><h2 class="article-body__section" id="section-weber-master-touch-review-performance"><span>Weber Master-Touch review: Performance</span></h2><p>The Master-Touch is a kettle grill, and it performs like, well, pretty much every other kettle grill. It’ll happily get up to 750F/400C or higher if you fuel it accordingly, making it a monster for those quick, powerful sears.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1831px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="23iHyrQx3Cag5R7RoEiLdP" name="Weber Master-Touch-18" alt="Food being cooked on the Weber Master-Touch" src="https://cdn.mos.cms.futurecdn.net/v2/t:41,l:0,cw:1831,ch:1030,q:80/23iHyrQx3Cag5R7RoEiLdP.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>If you’re just cooking on high heat, it’s a very straightforward and competent grill. The 22-inch cooking surface was enough space for me to simultaneously cook around 2lbs of chicken wings, several slices of pork belly, two burgers, two veggie skewers and two corns. Admittedly, space was a little tight — I had 4.5lbs of chicken in total, which had to be done in two batches — but I managed. </p><p>I arranged the fuel half-and-half for direct/indirect heat underneath the cooking grill. I had direct heat on one half at around 480-525F / 250-275C, with a lower indirect heat on the other half to slowly cook through the riskier meats. The fuel grate is positioned at the ideal level below the main grate — you only need a single layer of charcoal to get to 480F / 250C and higher under the cooking grate, enough for strong sears.</p><p>22 inches is (just) enough cooking space to let you get creative with your charcoal layout and create different heating zones. This is especially important for smoking, where you can use various layouts (like the charcoal snake method) to channel the fuel burn along a route, creating lower heat and smoke for longer. If you’re going to be smoking more, the 26-inch model will give you more space for your layouts, and is what I’d recommend. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="QbjAt8rqan3VPhPP4GTHSN" name="Weber Master-Touch-15" alt="The Weber Master-Touch thermometer" src="https://cdn.mos.cms.futurecdn.net/QbjAt8rqan3VPhPP4GTHSN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The ambient temperature dial on the top of the kettle is also a great help for smoking, letting you keep an eye on temperature, although again, you’ll want meat probes to smoke accurately. </p><p>For any serious smoking, though, I would advise investing in a dedicated smoker like the Searwood XL 600, Kamado Joe, or <a href="https://www.tomsguide.com/home/outdoors/traeger-woodridge-pro-grill-review">Traegar Woodridge Pro</a> ($999). Although pricey, these have features to make smoking much more accurate and consistent, while giving more control over taste — I’m talking real-time handling of fuel burn, smoke levels and temperature, etc. They also have apps, so you can control everything from afar. However, if, like me, you only smoke food occasionally, but mostly use a grill for searing, the Master-Touch is more than adequate.</p><h2 class="article-body__section" id="section-weber-master-touch-review-storage-maintenance"><span>Weber Master-Touch review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="fCMg4PZ438Pf5sMMMaQKrN" name="Weber Master-Touch-10" alt="The Weber Master-Touch One-Touch cleaning system" src="https://cdn.mos.cms.futurecdn.net/fCMg4PZ438Pf5sMMMaQKrN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>As I mentioned earlier, as far as grills go, the Master-Touch is fairly compact, making it easy to stash away in a neat spot on your patio, or under cover in your shed or garage. Weber gear is designed to withstand being kept outdoors, and indeed my Searwood has lived outside, uncovered for around a year, with only a modicum of rust showing up. Ideally, though, you’d want to store the Master-Touch (or any grill) under a cover to shield it from the elements.</p><p>The Master-Touch benefits from Weber’s generous warranty: 10 years on the bowl and lid; 5 years on the ash tray system and plastic components; and 2 years on everything else. Opt for the upgraded stainless grates, though, and those come with a 5-year warranty.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="sUPfXdJmoyLQUh8AVNFVJD" name="IMG_1932" alt="The Weber Master-Touch with the One-Touch cleaning system being used by hand" src="https://cdn.mos.cms.futurecdn.net/sUPfXdJmoyLQUh8AVNFVJD.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Maintenance is easy — another benefit of small grills. Clear the fuel out, dump the ash using the “One-Touch” system (which you’ll need to slide back and forth… around 10-15 times), then remove and clean the grills with BBQ cleaner, before repeating on the kettle and lid. I use Weber’s wire brush to remove large debris from the grill, then give it a good spray and wipe. For tougher-to-shift muck, I’ve put the grills in my dishwasher, which they can stand up to due to being stainless, but will discolor them over time (I don’t care, though, as they get blackened during cooking anyway). It took me around 10-15 minutes for a quick clean when I knew I was cooking again the next day, and around 30 minutes for a deeper clean.</p><p>Listen, I’m not moaning, because this was an easy grill to clean. But as you can see, to call it “One-Touch” is incredibly ambitious, to say the least.</p><h2 class="article-body__section" id="section-weber-master-touch-review-verdict"><span>Weber Master-Touch review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="J7bR2uE8njqzifDzyUwwxN" name="Weber Master-Touch-7" alt="The Weber Master-Touch on a lawn" src="https://cdn.mos.cms.futurecdn.net/J7bR2uE8njqzifDzyUwwxN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Like I said earlier, there’s value in simplicity. The Weber Master-Touch is a simple kettle grill with a few quality-of-life features. That means it’s not going to wow you with modern features, but it also makes it difficult to go wrong.</p><p>The Master-Touch cooks great, is customizable, and easy to both store and maintain. It gives you just enough space to get creative with fueling, allowing different temperature zones and smoking. And it gets super hot to put beautiful sears on food. Sure, the wheels feel a little flimsy, but the rest of the grill is built extremely well, and besides, the Master-Touch <em>is</em> an affordable grill. </p><p>I've been using the Master-Touch all summer here in the U.K., where we've had a heatwave (reaching up to 102F). Cooking inside in such conditions is awful (we don't have AC in our homes here), and I've really appreciate having the Master-Touch — its small size, quick heat up and ease-of-cleaning has let me cook al-fresco quickly and easily.</p><p>If it’s a compact, affordable but quality grilling solution you’re after for this summer, the Weber Master-Touch comes highly recommended by me.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Beat the barista: 9 De'Longhi espresso machines to upgrade your morning coffee routine ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Is there anything better than that first morning cup of coffee? A great cup of joe will wake you up and get you ready for the day ahead, all while tasting delicious. But if you're a serious fan then you'll know how borderline impossible it is to get a decent cup from shop-bought instant coffee.</p><p>That's where investing in a coffee machine comes in. A good espresso machine or drip coffee machine is an instant upgrade. They're also a great way to save money that you'd otherwise spend at your local coffee shop. </p><p>If you're looking to add one to your kitchen, then now is a great time to buy. De'Longhi is one of the best brands on the market and loads of its machines are on sale right now at Amazon. Check out the nine picks below, from good value budget offerings all the way up to feature-packed high-end models. </p>        <div class="featured_product_block featured_block_hero" data-id="2b1d1706-80fe-11f1-a9ba-6b7869aa21ad">            <a href="https://www.amazon.com/dp/B00KA8YC6A/" data-model-name="De'Longhi Dedica Espresso Machine with Manual Milk Frother" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/cmXJLFfihzJvcGsxHHmTn5.png" alt="De’Longhi Dedica Espresso Machine."></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De’Longhi</div>                                        <div class="featured__title">De'Longhi Dedica Espresso Machine with Manual Milk Frother</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e07d4-80fc-11f1-a4eb-7bd0f20bd492">            <a href="https://www.amazon.com/dp/B07QM274FC/" data-model-name="De'longhi 3-In-1 Specialty Coffee Brewer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rsumHv9feWT73Es6brHUXC.jpg" alt="De'longhi 3-In-1 Specialty Coffee Brewer, Icedcoffee Maker (bold Cold Brew), Gourmet Pour Over, Premium Drip, Sca Goldencup Certified, Glass Carafe, 8-Cup, Icm17270"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi 3-In-1 Specialty Coffee Brewer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e0856-80fc-11f1-a109-e3cfa6743c2e">            <a href="https://www.amazon.com/dp/B0F3S1JJ4T/" data-model-name="De'longhi Dedica Duo Compact Espresso Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/fJemG2aVLVSYexSzui8hgC.jpg" alt="De'longhi Dedica Duo Compact Espresso Machine - Cold Brew Coffee Maker With Milk Frother Wand- for Espresso, Hot & Iced Coffee, Cappuccinos, Lattes & Home Barista Brewing, Pistachio Green"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi Dedica Duo Compact Espresso Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e093c-80fc-11f1-9c85-8d9fab30233d">            <a href="https://www.amazon.com/dp/B09HLB4DP4/" data-model-name="De’longhi Dedica Arte Espresso Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/vVcaJnhRuyPUXfc6N5dnND.jpg" alt="De’longhi Dedica Arte Espresso Machine With My Latteart Steam Wand (milk Frother), 15-Bar Pump & 3-Level Temp Control - Compact Espresso Maker for Latte & Cappuccino - Italian Stainless Steel Design"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De’longhi Dedica Arte Espresso Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e09aa-80fc-11f1-a1e9-05ede0026da7">            <a href="https://www.amazon.com/dp/B08HRM697D/" data-model-name="De'longhi Upgraded All-In-One Combination Coffee Maker & Espresso Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:79.60%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/2JvBVuth9yug2sKeDhdBmD.jpg" alt="De'longhi Upgraded All-In-One Combination Coffee Maker & Espresso Machine + Advanced Adjustable Milk Frother for Cappuccino & Latte + Glass Coffee Pot 10-Cup, Com532m Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi Upgraded All-In-One Combination Coffee Maker & Espresso Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e0a18-80fc-11f1-8448-719596bdc035">            <a href="https://www.amazon.com/dp/B0CT2PT244/" data-model-name="De'longhi Dedica Maestro Plus Espresso Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/cVN53YRf88gL4CSC9SaD8F.jpg" alt="De'longhi Dedica Maestro Plus Espresso Machine, Automatic Steam Wand, 3 Coffee Temp & Milk Foam Settings, 54oz Removable Tank, Easy-To-Clean Dishwasher-Safe Parts, Compact Stainless Steel Design"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi Dedica Maestro Plus Espresso Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e0a86-80fc-11f1-938f-9510da637715">            <a href="https://www.amazon.com/dp/B0F3ZSVPPG/" data-model-name="De'longhi True Brew Drip Coffee Maker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/JaWYRC3w5vaKRxKUonVBmG.jpg" alt="De'longhi True Brew Drip Coffee Maker, Built in Grinder, Single Serve, 8 Oz to 24 Oz, Hot or Iced Coffee, Stainless, Cam51025mb"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi True Brew Drip Coffee Maker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e0af4-80fc-11f1-b394-1bd43d6fea03">            <a href="https://www.amazon.com/dp/B0B38KRTV6/" data-model-name="De'longhi Magnifica Evo Automatic Espresso & Coffee Machine With Auto Milk Frother" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/S7HYnNWyXnX9UtZievCG9H.jpg" alt="De'longhi Magnifica Evo Automatic Espresso & Coffee Machine With Auto Milk Frother, Built-In Grinder & Auto-Clean Function - for Latte, Cappuccino, Americano, Iced Coffee, Ecam29084sb , Silver"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi Magnifica Evo Automatic Espresso & Coffee Machine With Auto Milk Frother</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e0b62-80fc-11f1-a0ee-e34f217db709">            <a href="https://www.amazon.com/dp/B0F1H1CBC9/" data-model-name="De'longhi Rivelia Automatic Espresso Machine With Grinder & Milk Frother" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Yy3T5syLfsSCvdreUa7KmH.jpg" alt="De'longhi Rivelia Automatic Espresso Machine With Grinder & Milk Frother – All-In-One Coffee Maker, Hot & Iced Latte, Cappuccino, Bean Switch System, Burr Grinder, Compact Italian Design, White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi Rivelia Automatic Espresso Machine With Grinder & Milk Frother</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="2b1d194a-80fe-11f1-9646-2b5249c8ddbc">                        <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>BRAND</div>                                        <div class="featured__title">De’Longhi Dedica Espresso Machine with Manual Milk Frother, 15-Bar Pump & Adjustable Tray - Home Barista Coffee Maker for Espresso, Latte, Cappuccino...</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/beat-the-barista-9-delonghi-espresso-machines-to-upgrade-your-morning-coffee-routine</link>
                                                                            <description>
                            <![CDATA[ Upgrade your coffee game with one of these feature-packed coffee makers from  De'Longhi. Most of these machines have money off right now, too, making it a good time to buy. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">y7f9nSdFZaP4iJLZhy5eeS</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/HY9mru48BAJxHQ449bL2oL-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Thu, 16 Jul 2026 15:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                                    <dc:creator><![CDATA[ Will Salmon ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/HpRWP7Mh9ErgRGoTvZBbBJ.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Will Salmon is Tom&#039;s Guide&#039;s Collections Editor, curating advice on the best products to spend your money on. Will has been writing about tech and entertainment for almost 20 years, including stints as GamesRadar+&#039;s Streaming Editor, editing comics website Newsarama, and writing for lots and lots of magazines.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/png" url="https://cdn.mos.cms.futurecdn.net/HY9mru48BAJxHQ449bL2oL-1280-80.png">
                                                            <media:credit><![CDATA[De&#039;Longhi]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of De&#039;Longhi espresso machines in a kitchen.]]></media:description>                                                            <media:text><![CDATA[A selection of De&#039;Longhi espresso machines in a kitchen.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of De&#039;Longhi espresso machines in a kitchen.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/HY9mru48BAJxHQ449bL2oL-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Is there anything better than that first morning cup of coffee? A great cup of joe will wake you up and get you ready for the day ahead, all while tasting delicious. But if you're a serious fan then you'll know how borderline impossible it is to get a decent cup from shop-bought instant coffee.</p><p>That's where investing in a coffee machine comes in. A good espresso machine or drip coffee machine is an instant upgrade. They're also a great way to save money that you'd otherwise spend at your local coffee shop. </p><p>If you're looking to add one to your kitchen, then now is a great time to buy. De'Longhi is one of the best brands on the market and loads of its machines are on sale right now at Amazon. Check out the nine picks below, from good value budget offerings all the way up to feature-packed high-end models. </p>        <div class="featured_product_block featured_block_hero" data-id="2b1d1706-80fe-11f1-a9ba-6b7869aa21ad">            <a href="https://www.amazon.com/dp/B00KA8YC6A/" data-model-name="De'Longhi Dedica Espresso Machine with Manual Milk Frother" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/cmXJLFfihzJvcGsxHHmTn5.png" alt="De’Longhi Dedica Espresso Machine."></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De’Longhi</div>                                        <div class="featured__title">De'Longhi Dedica Espresso Machine with Manual Milk Frother</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e07d4-80fc-11f1-a4eb-7bd0f20bd492">            <a href="https://www.amazon.com/dp/B07QM274FC/" data-model-name="De'longhi 3-In-1 Specialty Coffee Brewer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rsumHv9feWT73Es6brHUXC.jpg" alt="De'longhi 3-In-1 Specialty Coffee Brewer, Icedcoffee Maker (bold Cold Brew), Gourmet Pour Over, Premium Drip, Sca Goldencup Certified, Glass Carafe, 8-Cup, Icm17270"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi 3-In-1 Specialty Coffee Brewer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e0856-80fc-11f1-a109-e3cfa6743c2e">            <a href="https://www.amazon.com/dp/B0F3S1JJ4T/" data-model-name="De'longhi Dedica Duo Compact Espresso Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/fJemG2aVLVSYexSzui8hgC.jpg" alt="De'longhi Dedica Duo Compact Espresso Machine - Cold Brew Coffee Maker With Milk Frother Wand- for Espresso, Hot & Iced Coffee, Cappuccinos, Lattes & Home Barista Brewing, Pistachio Green"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi Dedica Duo Compact Espresso Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e093c-80fc-11f1-9c85-8d9fab30233d">            <a href="https://www.amazon.com/dp/B09HLB4DP4/" data-model-name="De’longhi Dedica Arte Espresso Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/vVcaJnhRuyPUXfc6N5dnND.jpg" alt="De’longhi Dedica Arte Espresso Machine With My Latteart Steam Wand (milk Frother), 15-Bar Pump & 3-Level Temp Control - Compact Espresso Maker for Latte & Cappuccino - Italian Stainless Steel Design"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De’longhi Dedica Arte Espresso Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e09aa-80fc-11f1-a1e9-05ede0026da7">            <a href="https://www.amazon.com/dp/B08HRM697D/" data-model-name="De'longhi Upgraded All-In-One Combination Coffee Maker & Espresso Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:79.60%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/2JvBVuth9yug2sKeDhdBmD.jpg" alt="De'longhi Upgraded All-In-One Combination Coffee Maker & Espresso Machine + Advanced Adjustable Milk Frother for Cappuccino & Latte + Glass Coffee Pot 10-Cup, Com532m Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi Upgraded All-In-One Combination Coffee Maker & Espresso Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e0a18-80fc-11f1-8448-719596bdc035">            <a href="https://www.amazon.com/dp/B0CT2PT244/" data-model-name="De'longhi Dedica Maestro Plus Espresso Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/cVN53YRf88gL4CSC9SaD8F.jpg" alt="De'longhi Dedica Maestro Plus Espresso Machine, Automatic Steam Wand, 3 Coffee Temp & Milk Foam Settings, 54oz Removable Tank, Easy-To-Clean Dishwasher-Safe Parts, Compact Stainless Steel Design"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi Dedica Maestro Plus Espresso Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e0a86-80fc-11f1-938f-9510da637715">            <a href="https://www.amazon.com/dp/B0F3ZSVPPG/" data-model-name="De'longhi True Brew Drip Coffee Maker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/JaWYRC3w5vaKRxKUonVBmG.jpg" alt="De'longhi True Brew Drip Coffee Maker, Built in Grinder, Single Serve, 8 Oz to 24 Oz, Hot or Iced Coffee, Stainless, Cam51025mb"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi True Brew Drip Coffee Maker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e0af4-80fc-11f1-b394-1bd43d6fea03">            <a href="https://www.amazon.com/dp/B0B38KRTV6/" data-model-name="De'longhi Magnifica Evo Automatic Espresso & Coffee Machine With Auto Milk Frother" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/S7HYnNWyXnX9UtZievCG9H.jpg" alt="De'longhi Magnifica Evo Automatic Espresso & Coffee Machine With Auto Milk Frother, Built-In Grinder & Auto-Clean Function - for Latte, Cappuccino, Americano, Iced Coffee, Ecam29084sb , Silver"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi Magnifica Evo Automatic Espresso & Coffee Machine With Auto Milk Frother</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8a7e0b62-80fc-11f1-a0ee-e34f217db709">            <a href="https://www.amazon.com/dp/B0F1H1CBC9/" data-model-name="De'longhi Rivelia Automatic Espresso Machine With Grinder & Milk Frother" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Yy3T5syLfsSCvdreUa7KmH.jpg" alt="De'longhi Rivelia Automatic Espresso Machine With Grinder & Milk Frother – All-In-One Coffee Maker, Hot & Iced Latte, Cappuccino, Bean Switch System, Burr Grinder, Compact Italian Design, White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>De'Longhi</div>                                        <div class="featured__title">De'longhi Rivelia Automatic Espresso Machine With Grinder & Milk Frother</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="2b1d194a-80fe-11f1-9646-2b5249c8ddbc">                        <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>BRAND</div>                                        <div class="featured__title">De’Longhi Dedica Espresso Machine with Manual Milk Frother, 15-Bar Pump & Adjustable Tray - Home Barista Coffee Maker for Espresso, Latte, Cappuccino...</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Ninja just fixed the worst thing about microwaves — and it might replace your air fryer ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I have a strong feeling that how we use microwaves is about to change forever — and when I mention the name Ninja, you probably won’t be surprised. </p><p>The brand, known for dominating our countertops with some of the <a href="https://www.tomsguide.com/best-picks/best-air-fryers">best air fryers</a>, <a href="https://www.tomsguide.com/best-picks/best-blenders">blenders</a> and <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">coffee machines</a>, has never entered the microwave market, that is, until now.</p><p>Today, Ninja is once again shaking up modern cooking with the U.S. launch of the <strong>Ninja Crispi Microwave and Air Fryer</strong>,<strong> </strong>available for <a href="https://www.sharkninja.com/ninja-crispi-microwave/MW101.html?" target="_blank" rel="nofollow">$449 at SharkNinja.</a> It’s the anchor product behind the brand’s new summer product lineup, which also introduces the Ninja Crispi DualZone and Ninja Multipot.</p><h2 id="putting-an-end-to-soggy-microwave-meals">Putting an end to "soggy" microwave meals</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="PdUDLeYhePv89hfVTy9dMU" name="Ninja Crispi Microwave" alt="Ninja Crispi Microwave" src="https://cdn.mos.cms.futurecdn.net/PdUDLeYhePv89hfVTy9dMU.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Ninja)</span></figcaption></figure><p>It’s <strong>a significant leap for Ninja </strong>and one that I’ll be closely watching to see how it impacts the microwave market, including whether other kitchen appliance brands follow suit. </p><div class="product"><a data-dimension112="f0ab54c6-80f2-11f1-a0c6-97a644488e6e" data-action="Deal Block" data-label="Crispi Microwave and Air Fryer" data-dimension48="Crispi Microwave and Air Fryer" data-dimension25="$449" href="https://www.sharkninja.com/ninja-crispi-microwave-black/MW101BK1.html?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="AxzgozQZ8vf7hTwGowjYGU" name="Ninja Crispi Microwave" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/AxzgozQZ8vf7hTwGowjYGU.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.sharkninja.com/ninja-crispi-microwave-black/MW101BK1.html?" target="_blank" rel="nofollow" data-dimension112="f0ab54c6-80f2-11f1-a0c6-97a644488e6e" data-action="Deal Block" data-label="Crispi Microwave and Air Fryer" data-dimension48="Crispi Microwave and Air Fryer" data-dimension25="$449">Crispi Microwave and Air Fryer: $449</a></strong><br>Ninja's first microwave offers FusionCrisp technology and combines 1000W of microwave power with 1700W of air fryer power to deliver results 50% faster than a conventional oven. Skipping the traditional turntable design, it provides space for the accompanying 5.5-qt glass dish. It's available in Black and Silver.<a class="view-deal button" href="https://www.sharkninja.com/ninja-crispi-microwave-black/MW101BK1.html?" target="_blank" rel="nofollow" data-dimension112="f0ab54c6-80f2-11f1-a0c6-97a644488e6e" data-action="Deal Block" data-label="Crispi Microwave and Air Fryer" data-dimension48="Crispi Microwave and Air Fryer" data-dimension25="$449">View Deal</a></p></div><p>To put my interest into context, I’m a child of the ‘70s. I vividly remember my family’s awe when we got our first microwave. While my mom tried to cook <em>everything</em> in this ‘miracle’ oven, including roast chicken and meringue. I can clearly recall the artificial browning she used to color the pale chicken and the black carbon bottoms of her failed meringues. </p><p>Unsurprisingly, I’m far more conservative with my microwave adventures, and like most of us, I use it for simple tasks, such as defrosting, softening butter and warming up food.</p><p>But according to Ninja, those days are over, and the “soggy microwave problem we’ve accepted for years has officially been solved,” without sacrificing speed or texture. </p><h2 id="the-tech-behind-the-appliance">The tech behind the appliance</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="tbhH6ZCyQLnQVVQ9X8uDPB" name="Ninja Crispi Microwave" alt="Ninja Crispi Microwave" src="https://cdn.mos.cms.futurecdn.net/tbhH6ZCyQLnQVVQ9X8uDPB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Ninja)</span></figcaption></figure><p>The secret behind Ninja’s appliance is <strong>FusionCrisp Technology — combining microwave speed with superheated, cyclonic air at temperatures up to 450°F</strong>.</p><p>So how exactly does this work? The Ninji Crispi Microwave and Air Fryer starts by microwaving the food from the inside, then switches to air frying to produce a crisp texture, resulting in a cooking time 50% faster than a conventional oven. </p><p>Apart from giving crisp results, it offers <strong>14-in-1 functionality</strong>. The<strong> 8 microwave presets</strong> include popcorn, potato, defrost, beverage, soup, frozen dinner, frozen vegetable, and soften butter. There are a further <strong>5 air fry presets</strong>, including max crisp, crisp, air roast, air bake and air broil, each with FusionCrisp Technology.  </p><h2 id="ninja-ditches-the-traditional-turntable">Ninja ditches the traditional turntable</h2><p>Instead of the traditional rotating turntable, Ninja opted for a <strong>flatbed design</strong>, which it claims offers <strong>40% more space than a standard turntable microwave</strong>. That’s plenty of capacity to hold the <strong>5.5-quart glass basket</strong> that comes with the appliances, which is capable of holding an 8lb chicken, four pizza slices or a 9 x 11-inch casserole dish, feeding up to 8-10 people. </p><p>Although the Ninja Crispi Microwave and Air Fryer is now available in the U.S., U.K. readers will have to wait until mid-August for it to land.</p><p>I’m looking forward to testing and reviewing the Ninja Crispi Microwave and Air Fryer and will report back as soon as I get my hands on it. </p><p>In the meantime, I’ll have to rely on those of you in the U.S. to see how this exciting new product performs and whether you think it will shake up the microwave market. If you've tried one, leave a comment below with your thoughts.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">I tested the Ninja Crispi for a week and it made me rethink everything I knew about air fryers</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/ninja-creami-review">The Ninja Creami makes it so easy to make ice cream, it’s dangerous</a></li><li><a href="https://www.tomsguide.com/us/best-microwaves,review-6316.html">Best microwaves in 2026</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/ninja-just-fixed-the-worst-thing-about-microwaves-and-it-might-replace-your-air-fryer</link>
                                                                            <description>
                            <![CDATA[ Can Ninja solve the soggy microwave problem? A new Crispi appliance hits the market with that goal. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">b69kyrGupPYZdaPmGxDMhf</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/cRRV3hH9PBrgVZTFaah4g3-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 16 Jul 2026 11:17:03 +0000</pubDate>                                                                                                                                <updated>Thu, 16 Jul 2026 12:50:29 +0000</updated>
                                                                                                                                            <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ camilla.sharman@futurenet.com (Camilla Sharman) ]]></author>                    <dc:creator><![CDATA[ Camilla Sharman ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/nY4nvWzofHKHpvzAqN5LVH.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Camilla is the Homes Staff Writer and covers everything to do with homes and gardens. She has a wealth of editorial experience, mounting over 30 years, and covers news and features, tests products for reviews and compiles buying guides.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Her work has appeared in business and consumer titles, including Ideal Home, Real Homes, House Beautiful, Homebuilding &amp;amp; Renovation, and Kitchen &amp;amp; Bathroom Business. She’s even appeared on the cover of Your Home, writing about her own house renovation.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Although she’s obsessed with decorating her home, she also enjoys baking and trying out the latest kitchen appliances. But when she’s not inside, you’ll find her pottering about in her yard, tending to her vegetable patch or taking in her prized hydrangeas. She also enjoys keeping fit, and if she&#039;s not on a spin bike trying to keep up with the class, she&#039;ll be in the pool, or trying to perfect her headstand in a yoga class.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/cRRV3hH9PBrgVZTFaah4g3-1280-80.jpg">
                                                            <media:credit><![CDATA[Ninja]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Ninja Crispi Microwave]]></media:description>                                                            <media:text><![CDATA[Ninja Crispi Microwave]]></media:text>
                                <media:title type="plain"><![CDATA[Ninja Crispi Microwave]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/cRRV3hH9PBrgVZTFaah4g3-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I have a strong feeling that how we use microwaves is about to change forever — and when I mention the name Ninja, you probably won’t be surprised. </p><p>The brand, known for dominating our countertops with some of the <a href="https://www.tomsguide.com/best-picks/best-air-fryers">best air fryers</a>, <a href="https://www.tomsguide.com/best-picks/best-blenders">blenders</a> and <a href="https://www.tomsguide.com/home/coffee-makers/best-espresso-machines">coffee machines</a>, has never entered the microwave market, that is, until now.</p><p>Today, Ninja is once again shaking up modern cooking with the U.S. launch of the <strong>Ninja Crispi Microwave and Air Fryer</strong>,<strong> </strong>available for <a href="https://www.sharkninja.com/ninja-crispi-microwave/MW101.html?" target="_blank" rel="nofollow">$449 at SharkNinja.</a> It’s the anchor product behind the brand’s new summer product lineup, which also introduces the Ninja Crispi DualZone and Ninja Multipot.</p><h2 id="putting-an-end-to-soggy-microwave-meals">Putting an end to "soggy" microwave meals</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="PdUDLeYhePv89hfVTy9dMU" name="Ninja Crispi Microwave" alt="Ninja Crispi Microwave" src="https://cdn.mos.cms.futurecdn.net/PdUDLeYhePv89hfVTy9dMU.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Ninja)</span></figcaption></figure><p>It’s <strong>a significant leap for Ninja </strong>and one that I’ll be closely watching to see how it impacts the microwave market, including whether other kitchen appliance brands follow suit. </p><div class="product"><a data-dimension112="f0ab54c6-80f2-11f1-a0c6-97a644488e6e" data-action="Deal Block" data-label="Crispi Microwave and Air Fryer" data-dimension48="Crispi Microwave and Air Fryer" data-dimension25="$449" href="https://www.sharkninja.com/ninja-crispi-microwave-black/MW101BK1.html?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="AxzgozQZ8vf7hTwGowjYGU" name="Ninja Crispi Microwave" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/AxzgozQZ8vf7hTwGowjYGU.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.sharkninja.com/ninja-crispi-microwave-black/MW101BK1.html?" target="_blank" rel="nofollow" data-dimension112="f0ab54c6-80f2-11f1-a0c6-97a644488e6e" data-action="Deal Block" data-label="Crispi Microwave and Air Fryer" data-dimension48="Crispi Microwave and Air Fryer" data-dimension25="$449">Crispi Microwave and Air Fryer: $449</a></strong><br>Ninja's first microwave offers FusionCrisp technology and combines 1000W of microwave power with 1700W of air fryer power to deliver results 50% faster than a conventional oven. Skipping the traditional turntable design, it provides space for the accompanying 5.5-qt glass dish. It's available in Black and Silver.<a class="view-deal button" href="https://www.sharkninja.com/ninja-crispi-microwave-black/MW101BK1.html?" target="_blank" rel="nofollow" data-dimension112="f0ab54c6-80f2-11f1-a0c6-97a644488e6e" data-action="Deal Block" data-label="Crispi Microwave and Air Fryer" data-dimension48="Crispi Microwave and Air Fryer" data-dimension25="$449">View Deal</a></p></div><p>To put my interest into context, I’m a child of the ‘70s. I vividly remember my family’s awe when we got our first microwave. While my mom tried to cook <em>everything</em> in this ‘miracle’ oven, including roast chicken and meringue. I can clearly recall the artificial browning she used to color the pale chicken and the black carbon bottoms of her failed meringues. </p><p>Unsurprisingly, I’m far more conservative with my microwave adventures, and like most of us, I use it for simple tasks, such as defrosting, softening butter and warming up food.</p><p>But according to Ninja, those days are over, and the “soggy microwave problem we’ve accepted for years has officially been solved,” without sacrificing speed or texture. </p><h2 id="the-tech-behind-the-appliance">The tech behind the appliance</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="tbhH6ZCyQLnQVVQ9X8uDPB" name="Ninja Crispi Microwave" alt="Ninja Crispi Microwave" src="https://cdn.mos.cms.futurecdn.net/tbhH6ZCyQLnQVVQ9X8uDPB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Ninja)</span></figcaption></figure><p>The secret behind Ninja’s appliance is <strong>FusionCrisp Technology — combining microwave speed with superheated, cyclonic air at temperatures up to 450°F</strong>.</p><p>So how exactly does this work? The Ninji Crispi Microwave and Air Fryer starts by microwaving the food from the inside, then switches to air frying to produce a crisp texture, resulting in a cooking time 50% faster than a conventional oven. </p><p>Apart from giving crisp results, it offers <strong>14-in-1 functionality</strong>. The<strong> 8 microwave presets</strong> include popcorn, potato, defrost, beverage, soup, frozen dinner, frozen vegetable, and soften butter. There are a further <strong>5 air fry presets</strong>, including max crisp, crisp, air roast, air bake and air broil, each with FusionCrisp Technology.  </p><h2 id="ninja-ditches-the-traditional-turntable">Ninja ditches the traditional turntable</h2><p>Instead of the traditional rotating turntable, Ninja opted for a <strong>flatbed design</strong>, which it claims offers <strong>40% more space than a standard turntable microwave</strong>. That’s plenty of capacity to hold the <strong>5.5-quart glass basket</strong> that comes with the appliances, which is capable of holding an 8lb chicken, four pizza slices or a 9 x 11-inch casserole dish, feeding up to 8-10 people. </p><p>Although the Ninja Crispi Microwave and Air Fryer is now available in the U.S., U.K. readers will have to wait until mid-August for it to land.</p><p>I’m looking forward to testing and reviewing the Ninja Crispi Microwave and Air Fryer and will report back as soon as I get my hands on it. </p><p>In the meantime, I’ll have to rely on those of you in the U.S. to see how this exciting new product performs and whether you think it will shake up the microwave market. If you've tried one, leave a comment below with your thoughts.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">I tested the Ninja Crispi for a week and it made me rethink everything I knew about air fryers</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/ninja-creami-review">The Ninja Creami makes it so easy to make ice cream, it’s dangerous</a></li><li><a href="https://www.tomsguide.com/us/best-microwaves,review-6316.html">Best microwaves in 2026</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ 12 must-buy air fryers on sale at Amazon right now – up to 39% off Ninja, Chefman, and Instant Pot ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Air fryers are an increasingly essential addition to any kitchen. Compact and versatile, these miniature convection ovens are ideal for everything from roasting vegetables to cooking fries, and reheating food. And because they need less oil than conventional fryers they can be a – slightly – healthier option too.</p><p>If you've been planning to buy one then we have some good news for you: there are loads on sale at Amazon right now. For this article I've concentrated on bigger brands. Most of these air fryers are from Ninja, Instant Pot, and Chefman. These are top-rated products that you can rely on – and they've all got great savings attached to them, with reductions of up to 39% off.</p>        <div class="featured_product_block featured_block_hero" data-id="159eeee0-8054-11f1-8c82-6d2346846aea">            <a href="https://www.amazon.com/CHEFMAN-Mini-Air-Fryer-Space-Saving/dp/B0DC11YH4J/" data-model-name="Chefman 2 Quart Mini Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/2aJmxbFAd6mrNUydNACzsk.jpg" alt="Chefman 2 Qt Mini Air Fryer – Digital Space-Saving Compact Air Fryer With Nonstick and Dishwasher Safe Basket, Quick & Easy Meals in Minutes, Features Digital Timer and Shake Reminder – Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman 2 Quart Mini Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159eef94-8054-11f1-bcae-23f5f4cc515e">            <a href="https://www.amazon.com/Fits-anywhereTM-Kitchenware-EverGoodTM-Adjustable-Temperature/dp/B0CZPJ1833/" data-model-name="Bella 4 Quart Slim Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/vyCzDBstyavub6LkH39y8m.jpg" alt="Bella 4qt Slim Air Fryer, Fits-Anywhere™ Kitchenware, Evergood™ Ceramic Nonstick Coating, Adjustable Temperature, 6 Preset Cooking Options, 60 Min Auto Shutoff W Audible Tone, 3.3lb Capacity, Blossom"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>BELLA</div>                                        <div class="featured__title">Bella 4 Quart Slim Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef034-8054-11f1-8351-1b85a6ce9464">            <a href="https://www.amazon.com/Chefman-Air-Fryer-Technology-Touchscreen/dp/B0CNY1YVDD/" data-model-name="Chefman 6 Quart Compact Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ai6YEmUQp4W6RxfGPAwgTm.jpg" alt="Chefman Air Fryer – 6 Qt Compact Airfryer for Quick & Easy Meals in Minutes, Features Hi-Fry Technology for Extra Crisp, Touchscreen Controls With 4 Presets, Nonstick & Dishwasher Safe Basket - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman 6 Quart Compact Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef0c0-8054-11f1-bde0-f163bba9e78c">            <a href="https://www.amazon.com/CHEFMAN-Windows-Independent-Nonstick-Dishwasher/dp/B0CDY277D3/" data-model-name="Chefman 6 Quart Dual Basket Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/EsBUKCprjj9PdRcT3YoFrm.jpg" alt="Chefman 6 Quart Dual Basket Air Fryer Oven With Easy View Windows, Sync Finish, Hi-Fry, Auto Shutoff, 2 Independent 3qt Nonstick Dishwasher Safe Frying Baskets, Digital Double Air Fryer - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman 6 Quart Dual Basket Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef1b0-8054-11f1-9c9b-2327b365ea26">            <a href="https://www.amazon.com/CHEFMAN-Multifunction-Rotisserie-Dehydrator-Convection/dp/B08DL8WH9V/" data-model-name="Chefman Multifunctional Digital Air Fryer and Rotisserie" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:125.63%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/SRYF468xEryJay7kkdXzGn.jpg" alt="Chefman Multifunctional Digital Air Fryer+ Rotisserie, Dehydrator, Convection Oven, 17 Touch Screen Presets Fry, Roast, Dehydrate, Bake, Xl 10l Family Size, Auto Shutoff, Large Easy-View Window, Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman Multifunctional Digital Air Fryer and Rotisserie</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef23c-8054-11f1-91c2-0f5de2464625">            <a href="https://www.amazon.com/Ninja-Capacity-Dehydrate-Technology-AF141/dp/B0CSZ7WBYW/" data-model-name="Ninja 5 Quart Air Fryer With Air Crisp" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/XAfY2z8CG7vAC9RURuQDhn.jpg" alt="Ninja Air Fryer With Air Crisp | 5 Qt Capacity Fits Up to 4lbs of Fries | 4-In-1 Pro Air Fry, Roast, Reheat, Dehydrate | 400°f Max Temp | 1750 Watts | W/nonstick Basket & Crisper Plate | Grey | Af141"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ninja</div>                                        <div class="featured__title">Ninja 5 Quart Air Fryer With Air Crisp</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef2c8-8054-11f1-b4b4-3bf0b3f58a5b">            <a href="https://www.amazon.com/Chefman-Crispinator-capacity-TurboFry-Technology/dp/B0DZ9VFWYN/" data-model-name="Chefman Crispinator 8 Quart Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/236Nx8YfzZBifH9f6rPK5o.jpg" alt="Chefman Crispinator Air Fryer 6-In-1 With 8 Qt Capacity, 450°f Max Temp With Turbofry Pro Technology for Fast and Even Results, Viewing Window, Nonstick Ceramic Basket"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman Crispinator 8 Quart Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef34a-8054-11f1-9a23-db335b7bf9c3">            <a href="https://www.amazon.com/Instant-Technology-Functions-Dehydrates-Reheats/dp/B096N28SZP/" data-model-name="Instant Pot Vortex Plus 6 Quart Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jhaKdtfgMTyX7FHyiZQhWo.jpg" alt="Instant Pot Vortex Plus 6qt Air Fryer With Odor Erase Technology,6-In-1 Functions That Crisps,roasts, Broils,dehydrates, Bakes & Reheats, 100+in-App Recipes, From the Makers of Instant Pot,1700w,black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Instant Pot</div>                                        <div class="featured__title">Instant Pot Vortex Plus 6 Quart Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef3cc-8054-11f1-876c-c72a4b218054">            <a href="https://www.amazon.com/Instant-Pot-Customizable-Touchscreen-Dishwasher-Safe/dp/B07VM1FT8W/" data-model-name="Instant Pot Vortex 6 Quart XL Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/EZjh7KQ6M7m6hfahUzgaD.jpg" alt="Instant Pot Vortex 6qt Xl Air Fryer, 4-In-1 Functions That Crisps, Roasts, Reheats, Bakes for Quick Easy Meals, 100+ In-App Recipes, Is Dishwasher-Safe, From the Makers of Instant Pot, Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Instant Pot</div>                                        <div class="featured__title">Instant Pot Vortex 6 Quart XL Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef458-8054-11f1-b8f7-49d4a0aa001c">            <a href="https://www.amazon.com/Chefman-Minutes-Dehydrate-Extra-Crispy-Chicken/dp/B0FZLV3PTV/" data-model-name="Chefman Dual Air Fryer Oven" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/gqcgtp4wvLRGHumdkpmzZ.jpg" alt="Chefman Dual Air Fryer Oven With Dc Motor, 9 Qt. Double Basket Air Fryer With 450°f Max Temp for Meals in Minutes, Bake, Broil, Dehydrate, Reheat for Extra-Crispy Chicken, Fries, and More - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman Dual Air Fryer Oven</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef4da-8054-11f1-a396-73a54b0e5b4b">            <a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101SG/dp/B0DJFB6ZKM/" data-model-name="Ninja Crispi 4-In-1 Glass Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ji967wLx3MW4p5HDTHCYr.jpg" alt="Ninja Crispi 4-In-1 Glass Air Fryer | With Non-Toxic 4qt & 6-Cup Glass Containers & Lids | Microwave, Freezer, Dishwasher Safe | 450°f Max Temp |1500 Watt | Green | Fn101sg"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ninja</div>                                        <div class="featured__title">Ninja Crispi 4-In-1 Glass Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef55c-8054-11f1-a997-538a61d20832">            <a href="https://www.amazon.com/Ninja-DZ401-Independent-Dehydrate-Family-Sized/dp/B096X9LGJ1/" data-model-name="Ninja DZ401 Foodi 10 Quart Dualzone XL Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/veMeB5583D5bSiSj6KQv93.jpg" alt="Ninja Dz401 Foodi 10 Quart 6-In-1 Dualzone Xl 2-Basket Air Fryer With 2 Independent Frying Baskets, Match Cook & Smart Finish to Roast, Broil, Dehydrate for Quick, Easy Family-Sized Meals, Grey"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ninja</div>                                        <div class="featured__title">Ninja DZ401 Foodi 10 Quart Dualzone XL Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/air-fryers/12-must-buy-air-fryers-on-sale-at-amazon-right-now-up-to-39-percent-off-ninja-chefman-and-instant-pot</link>
                                                                            <description>
                            <![CDATA[ Air fryers make a great, compact, and versatile addition to your kitchen. We've rounded up some great offers on Ninja, Instant Pot, and more. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">6Syy76dCeTiRyXMwKieQr5</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/WMUyWw67in8wxhAkDz3cTD-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 15 Jul 2026 17:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Air Fryers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                                    <dc:creator><![CDATA[ Will Salmon ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/HpRWP7Mh9ErgRGoTvZBbBJ.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Will Salmon is Tom&#039;s Guide&#039;s Collections Editor, curating advice on the best products to spend your money on. Will has been writing about tech and entertainment for almost 20 years, including stints as GamesRadar+&#039;s Streaming Editor, editing comics website Newsarama, and writing for lots and lots of magazines.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/WMUyWw67in8wxhAkDz3cTD-1280-80.jpg">
                                                            <media:credit><![CDATA[Ninja, Chefman, Instapot / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of air fryers in the kitchen.]]></media:description>                                                            <media:text><![CDATA[A selection of air fryers in the kitchen.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of air fryers in the kitchen.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/WMUyWw67in8wxhAkDz3cTD-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Air fryers are an increasingly essential addition to any kitchen. Compact and versatile, these miniature convection ovens are ideal for everything from roasting vegetables to cooking fries, and reheating food. And because they need less oil than conventional fryers they can be a – slightly – healthier option too.</p><p>If you've been planning to buy one then we have some good news for you: there are loads on sale at Amazon right now. For this article I've concentrated on bigger brands. Most of these air fryers are from Ninja, Instant Pot, and Chefman. These are top-rated products that you can rely on – and they've all got great savings attached to them, with reductions of up to 39% off.</p>        <div class="featured_product_block featured_block_hero" data-id="159eeee0-8054-11f1-8c82-6d2346846aea">            <a href="https://www.amazon.com/CHEFMAN-Mini-Air-Fryer-Space-Saving/dp/B0DC11YH4J/" data-model-name="Chefman 2 Quart Mini Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/2aJmxbFAd6mrNUydNACzsk.jpg" alt="Chefman 2 Qt Mini Air Fryer – Digital Space-Saving Compact Air Fryer With Nonstick and Dishwasher Safe Basket, Quick & Easy Meals in Minutes, Features Digital Timer and Shake Reminder – Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman 2 Quart Mini Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159eef94-8054-11f1-bcae-23f5f4cc515e">            <a href="https://www.amazon.com/Fits-anywhereTM-Kitchenware-EverGoodTM-Adjustable-Temperature/dp/B0CZPJ1833/" data-model-name="Bella 4 Quart Slim Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/vyCzDBstyavub6LkH39y8m.jpg" alt="Bella 4qt Slim Air Fryer, Fits-Anywhere™ Kitchenware, Evergood™ Ceramic Nonstick Coating, Adjustable Temperature, 6 Preset Cooking Options, 60 Min Auto Shutoff W Audible Tone, 3.3lb Capacity, Blossom"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>BELLA</div>                                        <div class="featured__title">Bella 4 Quart Slim Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef034-8054-11f1-8351-1b85a6ce9464">            <a href="https://www.amazon.com/Chefman-Air-Fryer-Technology-Touchscreen/dp/B0CNY1YVDD/" data-model-name="Chefman 6 Quart Compact Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ai6YEmUQp4W6RxfGPAwgTm.jpg" alt="Chefman Air Fryer – 6 Qt Compact Airfryer for Quick & Easy Meals in Minutes, Features Hi-Fry Technology for Extra Crisp, Touchscreen Controls With 4 Presets, Nonstick & Dishwasher Safe Basket - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman 6 Quart Compact Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef0c0-8054-11f1-bde0-f163bba9e78c">            <a href="https://www.amazon.com/CHEFMAN-Windows-Independent-Nonstick-Dishwasher/dp/B0CDY277D3/" data-model-name="Chefman 6 Quart Dual Basket Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/EsBUKCprjj9PdRcT3YoFrm.jpg" alt="Chefman 6 Quart Dual Basket Air Fryer Oven With Easy View Windows, Sync Finish, Hi-Fry, Auto Shutoff, 2 Independent 3qt Nonstick Dishwasher Safe Frying Baskets, Digital Double Air Fryer - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman 6 Quart Dual Basket Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef1b0-8054-11f1-9c9b-2327b365ea26">            <a href="https://www.amazon.com/CHEFMAN-Multifunction-Rotisserie-Dehydrator-Convection/dp/B08DL8WH9V/" data-model-name="Chefman Multifunctional Digital Air Fryer and Rotisserie" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:125.63%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/SRYF468xEryJay7kkdXzGn.jpg" alt="Chefman Multifunctional Digital Air Fryer+ Rotisserie, Dehydrator, Convection Oven, 17 Touch Screen Presets Fry, Roast, Dehydrate, Bake, Xl 10l Family Size, Auto Shutoff, Large Easy-View Window, Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman Multifunctional Digital Air Fryer and Rotisserie</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef23c-8054-11f1-91c2-0f5de2464625">            <a href="https://www.amazon.com/Ninja-Capacity-Dehydrate-Technology-AF141/dp/B0CSZ7WBYW/" data-model-name="Ninja 5 Quart Air Fryer With Air Crisp" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/XAfY2z8CG7vAC9RURuQDhn.jpg" alt="Ninja Air Fryer With Air Crisp | 5 Qt Capacity Fits Up to 4lbs of Fries | 4-In-1 Pro Air Fry, Roast, Reheat, Dehydrate | 400°f Max Temp | 1750 Watts | W/nonstick Basket & Crisper Plate | Grey | Af141"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ninja</div>                                        <div class="featured__title">Ninja 5 Quart Air Fryer With Air Crisp</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef2c8-8054-11f1-b4b4-3bf0b3f58a5b">            <a href="https://www.amazon.com/Chefman-Crispinator-capacity-TurboFry-Technology/dp/B0DZ9VFWYN/" data-model-name="Chefman Crispinator 8 Quart Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/236Nx8YfzZBifH9f6rPK5o.jpg" alt="Chefman Crispinator Air Fryer 6-In-1 With 8 Qt Capacity, 450°f Max Temp With Turbofry Pro Technology for Fast and Even Results, Viewing Window, Nonstick Ceramic Basket"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman Crispinator 8 Quart Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef34a-8054-11f1-9a23-db335b7bf9c3">            <a href="https://www.amazon.com/Instant-Technology-Functions-Dehydrates-Reheats/dp/B096N28SZP/" data-model-name="Instant Pot Vortex Plus 6 Quart Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jhaKdtfgMTyX7FHyiZQhWo.jpg" alt="Instant Pot Vortex Plus 6qt Air Fryer With Odor Erase Technology,6-In-1 Functions That Crisps,roasts, Broils,dehydrates, Bakes & Reheats, 100+in-App Recipes, From the Makers of Instant Pot,1700w,black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Instant Pot</div>                                        <div class="featured__title">Instant Pot Vortex Plus 6 Quart Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef3cc-8054-11f1-876c-c72a4b218054">            <a href="https://www.amazon.com/Instant-Pot-Customizable-Touchscreen-Dishwasher-Safe/dp/B07VM1FT8W/" data-model-name="Instant Pot Vortex 6 Quart XL Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/EZjh7KQ6M7m6hfahUzgaD.jpg" alt="Instant Pot Vortex 6qt Xl Air Fryer, 4-In-1 Functions That Crisps, Roasts, Reheats, Bakes for Quick Easy Meals, 100+ In-App Recipes, Is Dishwasher-Safe, From the Makers of Instant Pot, Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Instant Pot</div>                                        <div class="featured__title">Instant Pot Vortex 6 Quart XL Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef458-8054-11f1-b8f7-49d4a0aa001c">            <a href="https://www.amazon.com/Chefman-Minutes-Dehydrate-Extra-Crispy-Chicken/dp/B0FZLV3PTV/" data-model-name="Chefman Dual Air Fryer Oven" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/gqcgtp4wvLRGHumdkpmzZ.jpg" alt="Chefman Dual Air Fryer Oven With Dc Motor, 9 Qt. Double Basket Air Fryer With 450°f Max Temp for Meals in Minutes, Bake, Broil, Dehydrate, Reheat for Extra-Crispy Chicken, Fries, and More - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Chefman</div>                                        <div class="featured__title">Chefman Dual Air Fryer Oven</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef4da-8054-11f1-a396-73a54b0e5b4b">            <a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101SG/dp/B0DJFB6ZKM/" data-model-name="Ninja Crispi 4-In-1 Glass Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ji967wLx3MW4p5HDTHCYr.jpg" alt="Ninja Crispi 4-In-1 Glass Air Fryer | With Non-Toxic 4qt & 6-Cup Glass Containers & Lids | Microwave, Freezer, Dishwasher Safe | 450°f Max Temp |1500 Watt | Green | Fn101sg"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ninja</div>                                        <div class="featured__title">Ninja Crispi 4-In-1 Glass Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="159ef55c-8054-11f1-a997-538a61d20832">            <a href="https://www.amazon.com/Ninja-DZ401-Independent-Dehydrate-Family-Sized/dp/B096X9LGJ1/" data-model-name="Ninja DZ401 Foodi 10 Quart Dualzone XL Air Fryer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/veMeB5583D5bSiSj6KQv93.jpg" alt="Ninja Dz401 Foodi 10 Quart 6-In-1 Dualzone Xl 2-Basket Air Fryer With 2 Independent Frying Baskets, Match Cook & Smart Finish to Roast, Broil, Dehydrate for Quick, Easy Family-Sized Meals, Grey"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ninja</div>                                        <div class="featured__title">Ninja DZ401 Foodi 10 Quart Dualzone XL Air Fryer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Watch out KitchenAid! Ooni’s launching its second stand mixer, and at $599 its price matches the Artisan Pro ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Ooni is strengthening its position in the stand mixer market with the imminent launch of its second mixer — the <a href="https://www.tomsguide.com/home/home-appliances/i-got-a-first-look-at-oonis-halo-core-spiral-mixer-three-things-i-love-and-one-thing-i-dont">Halo Core</a>. Look out, <a href="https://www.tomsguide.com/home/kitchen-dining/i-got-my-first-kitchenaid-artisan-stand-mixer-last-month-heres-3-things-ive-learned">KitchenAid</a>, you’ve got some serious competition!</p><p>The brand behind some of the <a href="https://www.tomsguide.com/best-picks/best-outdoor-pizza-ovens">best pizza ovens</a> is once again treating pizza lovers to an all-round experience by developing technology to make the perfect dough.</p><p>Having just launched the debut <a href="https://www.tomsguide.com/home/home-appliances/ooni-halo-pro-spiral-mixer-release-first-look">Halo Pro</a> in April 2025, I can only imagine the engineers at Ooni were back on the case as soon as the brand’s first <a href="https://www.tomsguide.com/best-picks/best-stand-mixers">stand mixer</a> left the factory.</p><h2 id="perfecting-dough-for-home-bakers">Perfecting dough for home bakers</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="sP5cMU5JRBmTP4X6KgfcXQ" name="Lifestyle  Halo Core Pumpkin Dough_Foundry Black" alt="Ooni Halo Core stand mixer" src="https://cdn.mos.cms.futurecdn.net/sP5cMU5JRBmTP4X6KgfcXQ.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Ooni)</span></figcaption></figure><p>So, what’s so special about Ooni’s stand mixer offer, and how does the Halo Core differ from the first rendition?</p><p>Rather than the stand mixers we’ve become accustomed to, which use a planetary mixing design, <strong>Ooni has developed a spiral mixing technique traditionally reserved for professional kitchens and artisan bakeries</strong>. But what does this really mean?</p><p>Rather than the bowl staying put and the attachments spinning, Ooni’s bowl rotates.</p><p>Ooni claims the engineering “delivers exceptional gluten development and dough quality, helping bridge the gap between everyday baking and bakery-quality results.”</p><p>And Ooni adds: “Home bakers have blamed themselves for dense loaves and failed rises. The real culprit is the mixer. Planetary stand mixers, largely unchanged since their invention, were never built for dough. They stall, overheat, and can’t develop the gluten structures that separate a good bake from a great one. Ooni Halo Core changes that.”</p><h2 id="kitchenaid-has-some-tough-competition">KitchenAid has some tough competition</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="r63kVVf4qtrGc6rDLJt9oD" name="Ooni Halo Core stand mixer" alt="Ooni Halo Core stand mixer" src="https://cdn.mos.cms.futurecdn.net/r63kVVf4qtrGc6rDLJt9oD.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>When you see the stand mixer for yourself, you’ll instantly notice its professional look and heft! Although it’s not the size of a mixer you’d find in a professional kitchen, despite its pro appeal, it’s surprisingly compact. When I placed it next to the <a href="https://www.tomsguide.com/home/ive-never-given-a-product-a-5-star-rating-heres-why-kitchenaids-new-stand-mixer-won-me-over">KitchenAid Artisan Pro</a>, there was little difference in size; in fact, the KitchenAid is taller. </p><p>And that’s where <strong>the Halo Core differs from its older sibling, the Halo Pro: It’s 20% smaller and 25% lighter</strong>. But don’t let that fool you into thinking it’s easy to maneuver; it’s still 23.2 lbs. </p><p>What’s more, where the Halo Pros aimed at serious bakers and semi-professionals, the new Halo Core is targeted at home bakers. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Oajm5X"></div>                            </div>                            <script src="https://kwizly.com/embed/Oajm5X.js" async></script><h2 id="breadmaking-and-more-besides">Breadmaking and more besides</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="PojSFw8QqGmgEEmhJQiRoD" name="Ooni Halo Core stand mixer" alt="Ooni Halo Core stand mixer" src="https://cdn.mos.cms.futurecdn.net/PojSFw8QqGmgEEmhJQiRoD.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>The price is still on the high end, at $599 (£499), hitting the exact spot where the KitchenAid Artisan Pro sits in the market, but you are getting an exceptional machine — which I’ll be reviewing over the coming weeks.  </p><p>But what exactly does the Halo Core offer? <strong>You get a 4.7-quart stainless steel bowl with a silicone lid and pouring shield, 20 speed levels with up to 300 rpm and 1,000 rpm when using the geared whisk, an LED speed indicator and LED bowl lights. </strong></p><p>And with all of that comes a spiral dough hook and removable breaker bar to create a dual-kneading action that stretches and develops dough for a strong gluten structure. </p><p>But even though the Halo Core is designed to mix over 6lbs of dough, enough for 10 12-inch pizzas or three large sourdough loaves, it's not only designed by bread makers. It also comes with whipping and beating attachments.</p><p>But before you get too excited and race out to buy the Ooni Halo Core, it won’t be dropping in stores until 4 August, although you can <a href="https://uk.ooni.com/" target="_blank">join the waitlist</a> to ensure you don't miss out.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchenaid-vs-cuisinart-which-stand-mixer-is-best">KitchenAid vs Cuisinart: Which stand mixer is best?</a></li><li><a href="https://www.tomsguide.com/home/smegs-newest-stand-mixer-gives-kitchenaid-a-run-for-its-money-heres-why-it-won-me-over">Smeg’s newest retro stand mixer gives KitchenAid a run for its money — here’s why it won me over</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/i-tested-oonis-new-rotating-stone-and-its-your-ticket-to-perfect-pizza-at-home">I tested Ooni's new Rotating Stone — and it's your ticket to perfect pizza at home</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/watch-out-kitchenaid-oonis-launching-its-second-stand-mixer-and-at-usd599-it-price-matches-the-artisan-pro</link>
                                                                            <description>
                            <![CDATA[ Ooni’s new $599 spiral mixer is a dough-kneading beast and perfect for domestic kitchens. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">WxsLRxEyhnhgM7xgBbB9Tm</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/VArZ7rnBQ3pcwGHtvCxmg5-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 15 Jul 2026 10:09:41 +0000</pubDate>                                                                                                                                <updated>Thu, 10 Sep 2026 10:57:43 +0000</updated>
                                                                                                                                            <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ camilla.sharman@futurenet.com (Camilla Sharman) ]]></author>                    <dc:creator><![CDATA[ Camilla Sharman ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/nY4nvWzofHKHpvzAqN5LVH.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Camilla is the Homes Staff Writer and covers everything to do with homes and gardens. She has a wealth of editorial experience, mounting over 30 years, and covers news and features, tests products for reviews and compiles buying guides.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Her work has appeared in business and consumer titles, including Ideal Home, Real Homes, House Beautiful, Homebuilding &amp;amp; Renovation, and Kitchen &amp;amp; Bathroom Business. She’s even appeared on the cover of Your Home, writing about her own house renovation.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Although she’s obsessed with decorating her home, she also enjoys baking and trying out the latest kitchen appliances. But when she’s not inside, you’ll find her pottering about in her yard, tending to her vegetable patch or taking in her prized hydrangeas. She also enjoys keeping fit, and if she&#039;s not on a spin bike trying to keep up with the class, she&#039;ll be in the pool, or trying to perfect her headstand in a yoga class.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/VArZ7rnBQ3pcwGHtvCxmg5-1280-80.jpg">
                                                            <media:credit><![CDATA[Ooni]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Lifestyle  Halo Core Hero_Foundry Black]]></media:description>                                                            <media:text><![CDATA[Lifestyle  Halo Core Hero_Foundry Black]]></media:text>
                                <media:title type="plain"><![CDATA[Lifestyle  Halo Core Hero_Foundry Black]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/VArZ7rnBQ3pcwGHtvCxmg5-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Ooni is strengthening its position in the stand mixer market with the imminent launch of its second mixer — the <a href="https://www.tomsguide.com/home/home-appliances/i-got-a-first-look-at-oonis-halo-core-spiral-mixer-three-things-i-love-and-one-thing-i-dont">Halo Core</a>. Look out, <a href="https://www.tomsguide.com/home/kitchen-dining/i-got-my-first-kitchenaid-artisan-stand-mixer-last-month-heres-3-things-ive-learned">KitchenAid</a>, you’ve got some serious competition!</p><p>The brand behind some of the <a href="https://www.tomsguide.com/best-picks/best-outdoor-pizza-ovens">best pizza ovens</a> is once again treating pizza lovers to an all-round experience by developing technology to make the perfect dough.</p><p>Having just launched the debut <a href="https://www.tomsguide.com/home/home-appliances/ooni-halo-pro-spiral-mixer-release-first-look">Halo Pro</a> in April 2025, I can only imagine the engineers at Ooni were back on the case as soon as the brand’s first <a href="https://www.tomsguide.com/best-picks/best-stand-mixers">stand mixer</a> left the factory.</p><h2 id="perfecting-dough-for-home-bakers">Perfecting dough for home bakers</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="sP5cMU5JRBmTP4X6KgfcXQ" name="Lifestyle  Halo Core Pumpkin Dough_Foundry Black" alt="Ooni Halo Core stand mixer" src="https://cdn.mos.cms.futurecdn.net/sP5cMU5JRBmTP4X6KgfcXQ.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Ooni)</span></figcaption></figure><p>So, what’s so special about Ooni’s stand mixer offer, and how does the Halo Core differ from the first rendition?</p><p>Rather than the stand mixers we’ve become accustomed to, which use a planetary mixing design, <strong>Ooni has developed a spiral mixing technique traditionally reserved for professional kitchens and artisan bakeries</strong>. But what does this really mean?</p><p>Rather than the bowl staying put and the attachments spinning, Ooni’s bowl rotates.</p><p>Ooni claims the engineering “delivers exceptional gluten development and dough quality, helping bridge the gap between everyday baking and bakery-quality results.”</p><p>And Ooni adds: “Home bakers have blamed themselves for dense loaves and failed rises. The real culprit is the mixer. Planetary stand mixers, largely unchanged since their invention, were never built for dough. They stall, overheat, and can’t develop the gluten structures that separate a good bake from a great one. Ooni Halo Core changes that.”</p><h2 id="kitchenaid-has-some-tough-competition">KitchenAid has some tough competition</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="r63kVVf4qtrGc6rDLJt9oD" name="Ooni Halo Core stand mixer" alt="Ooni Halo Core stand mixer" src="https://cdn.mos.cms.futurecdn.net/r63kVVf4qtrGc6rDLJt9oD.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>When you see the stand mixer for yourself, you’ll instantly notice its professional look and heft! Although it’s not the size of a mixer you’d find in a professional kitchen, despite its pro appeal, it’s surprisingly compact. When I placed it next to the <a href="https://www.tomsguide.com/home/ive-never-given-a-product-a-5-star-rating-heres-why-kitchenaids-new-stand-mixer-won-me-over">KitchenAid Artisan Pro</a>, there was little difference in size; in fact, the KitchenAid is taller. </p><p>And that’s where <strong>the Halo Core differs from its older sibling, the Halo Pro: It’s 20% smaller and 25% lighter</strong>. But don’t let that fool you into thinking it’s easy to maneuver; it’s still 23.2 lbs. </p><p>What’s more, where the Halo Pros aimed at serious bakers and semi-professionals, the new Halo Core is targeted at home bakers. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Oajm5X"></div>                            </div>                            <script src="https://kwizly.com/embed/Oajm5X.js" async></script><h2 id="breadmaking-and-more-besides">Breadmaking and more besides</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="PojSFw8QqGmgEEmhJQiRoD" name="Ooni Halo Core stand mixer" alt="Ooni Halo Core stand mixer" src="https://cdn.mos.cms.futurecdn.net/PojSFw8QqGmgEEmhJQiRoD.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Camilla Sharman)</span></figcaption></figure><p>The price is still on the high end, at $599 (£499), hitting the exact spot where the KitchenAid Artisan Pro sits in the market, but you are getting an exceptional machine — which I’ll be reviewing over the coming weeks.  </p><p>But what exactly does the Halo Core offer? <strong>You get a 4.7-quart stainless steel bowl with a silicone lid and pouring shield, 20 speed levels with up to 300 rpm and 1,000 rpm when using the geared whisk, an LED speed indicator and LED bowl lights. </strong></p><p>And with all of that comes a spiral dough hook and removable breaker bar to create a dual-kneading action that stretches and develops dough for a strong gluten structure. </p><p>But even though the Halo Core is designed to mix over 6lbs of dough, enough for 10 12-inch pizzas or three large sourdough loaves, it's not only designed by bread makers. It also comes with whipping and beating attachments.</p><p>But before you get too excited and race out to buy the Ooni Halo Core, it won’t be dropping in stores until 4 August, although you can <a href="https://uk.ooni.com/" target="_blank">join the waitlist</a> to ensure you don't miss out.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/kitchenaid-vs-cuisinart-which-stand-mixer-is-best">KitchenAid vs Cuisinart: Which stand mixer is best?</a></li><li><a href="https://www.tomsguide.com/home/smegs-newest-stand-mixer-gives-kitchenaid-a-run-for-its-money-heres-why-it-won-me-over">Smeg’s newest retro stand mixer gives KitchenAid a run for its money — here’s why it won me over</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/i-tested-oonis-new-rotating-stone-and-its-your-ticket-to-perfect-pizza-at-home">I tested Ooni's new Rotating Stone — and it's your ticket to perfect pizza at home</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ 13 Shark and Ninja deals that'll make your dorm feel more like home — back to school appliances from $79 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>It's been several years since I lived in a dorm — but if I were shopping for back to school season today, I'd be super excited about the latest appliances from Ninja and Shark. From portable smoothie makers and misting fans to cordless vacuums and multifunctional hair tools, these space-saving essentials make dorm life simpler. </p><p>If you're gearing up for the new semester, I recommend <a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow">grabbing some Shark and Ninja appliances</a> — especially right now since Amazon is knocking up to 40% off select models. For instance, <a href="https://www.amazon.com/Shark-FlexBreeze-Fan-Ultra-powerful-evaporative-FA052/dp/B0DXRCXX3H?" target="_blank" rel="nofollow">the Shark FlexBreeze HydroGo is currently over $50 off</a> and is perfect for keep you cool while hanging out, studying or sleeping in the dorm.</p><p>There's plenty more convenient devices for back to school season below — and they start at just $79. Keep scrolling to check out all my favorite Shark and Ninja appliance deals on Amazon.</p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><strong></strong><a href="https://www.sharkninja.com/sales-offers/shop-all-sale" target="_blank" rel="nofollow"><strong>shop all SharkNinja deals</strong></a></li><li><a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow"><strong>shop all SharkNinja deals at Amazon</strong></a></li><li><strong>Shark Steam Pocket Mop: </strong><a href="https://www.amazon.com/Shark-Pocket-Cleaner-Steering-S3501/dp/B0028MB3HM" target="_blank" rel="nofollow"><strong>was $99 now $79</strong></a></li><li><strong>Ninja Blast Max: </strong><a href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow"><strong>was $99 now $79</strong></a><strong></strong></li><li><strong>Ninja Iced Coffee Maker: </strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LNKN4Z" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Shark FlexBreeze HydroGo: </strong><a href="https://www.amazon.com/Shark-FlexBreeze-Fan-Ultra-powerful-evaporative-FA052/dp/B0DXRCXX3H?" target="_blank" rel="nofollow"><strong>was $149 now $99</strong></a></li><li><strong>Ninja Mega Kitchen System: </strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><strong>was $219 now $149</strong></a></li><li><strong>Ninja Crispi 4-in-1 Portable Glass Air Fryer: </strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><strong>was $179 now $159</strong></a></li><li><strong>Shark FlexStyle: </strong><a href="https://www.amazon.com/dp/B0BBSFTJG5?" target="_blank" rel="nofollow"><strong>was $349 now $259</strong></a></li></ul><h3 class="article-body__section" id="section-ninja-shark-deals"><span>Ninja/Shark Deals</span></h3><div class="product"><a data-dimension112="b574e512-7f8e-11f1-a9f7-d1a1f6c7f145" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69" href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1216px;"><p class="vanilla-image-block" style="padding-top:123.36%;"><img id="9Q4QY399FaozAFUt4j8dTW" name="ninja-blast-max-portable-blender--twist--bb517479-54ce-4a77-974c-58e2f44dde33.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/9Q4QY399FaozAFUt4j8dTW.jpg" mos="" align="middle" fullscreen="" width="1216" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Blast Max is capable of taking on any challenge you throw at it, including crushing ice and blitzing berries to make yummy smoothies. It comes with multiple blend settings to make sure your blender is using the right level of power for the task at hand, and the compact size means it’s a great option for anyone with a small kitchen. <a class="view-deal button" href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow" data-dimension112="b574e512-7f8e-11f1-a9f7-d1a1f6c7f145" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69">View Deal</a></p></div><div class="product"><a data-dimension112="b574e576-7f8e-11f1-88de-39c75248c5d0" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79" href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1614px;"><p class="vanilla-image-block" style="padding-top:86.00%;"><img id="XofzJVhShmZ3kcVxuocQvB" name="Ninja Belgian Waffle Maker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/XofzJVhShmZ3kcVxuocQvB.png" mos="" align="middle" fullscreen="" width="1614" height="1388" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow" data-dimension112="b574e576-7f8e-11f1-88de-39c75248c5d0" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79">Belgian Waffle Maker: was $99 now $79</a></strong><br>Weekend brunch just got an upgrade. This vertical waffle maker has five shade settings so everyone gets their waffle exactly how they like it, plus a specialty setting for high-sugar batters like brownies (yes, really). The precision-pour cup takes the guesswork out of measuring, and audible alerts mean no more burnt disasters. Pair it with the Ninja CREAMi for the ultimate breakfast-dessert combo. <a class="view-deal button" href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow" data-dimension112="b574e576-7f8e-11f1-88de-39c75248c5d0" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79">View Deal</a></p></div><div class="product"><a data-dimension112="b574e5e4-7f8e-11f1-8564-799eca30cc34" data-action="Deal Block" data-label="Steam Pocket Mop Hard Floor Cleaner" data-dimension48="Steam Pocket Mop Hard Floor Cleaner" data-dimension25="$79" href="https://www.amazon.com/Shark-Pocket-Cleaner-Steering-S3501/dp/B0028MB3HM" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:343px;"><p class="vanilla-image-block" style="padding-top:256.27%;"><img id="xuU833BnMdHYwfAXhFZQvf" name="Shark Steam Pocket Mop Hard Floor Cleaner.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/xuU833BnMdHYwfAXhFZQvf.jpg" mos="" align="middle" fullscreen="" width="343" height="879" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Shark-Pocket-Cleaner-Steering-S3501/dp/B0028MB3HM" target="_blank" rel="nofollow" data-dimension112="b574e5e4-7f8e-11f1-8564-799eca30cc34" data-action="Deal Block" data-label="Steam Pocket Mop Hard Floor Cleaner" data-dimension48="Steam Pocket Mop Hard Floor Cleaner" data-dimension25="$79">Steam Pocket Mop Hard Floor Cleaner: was $99 now $79</a></strong><br>With the Shark Steam Pocket Mop, mopping might just be your new favorite household chore. Designed for sealed hard floors like hardwood, marble, tile, stone, laminate and vinyl, the mop transforms water into super-heated steam in seconds to remove dirt, grime, and hard-to-clean messes. This will help you remove up to 99% of household bacteria — without the harsh chemicals. <a class="view-deal button" href="https://www.amazon.com/Shark-Pocket-Cleaner-Steering-S3501/dp/B0028MB3HM" target="_blank" rel="nofollow" data-dimension112="b574e5e4-7f8e-11f1-8564-799eca30cc34" data-action="Deal Block" data-label="Steam Pocket Mop Hard Floor Cleaner" data-dimension48="Steam Pocket Mop Hard Floor Cleaner" data-dimension25="$79">View Deal</a></p></div><div class="product"><a data-dimension112="d00a8d38-7f92-11f1-afcb-7d930b89fd78" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LNKN4Z" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:99.56%;"><img id="4uiaRXRKY8Go6F4ZpM7UdA" name="Ninja coffee" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4uiaRXRKY8Go6F4ZpM7UdA.jpg" mos="" align="middle" fullscreen="" width="679" height="676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LNKN4Z" target="_blank" rel="nofollow" data-dimension112="d00a8d38-7f92-11f1-afcb-7d930b89fd78" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">Pods & Grounds Specialty Single-Serve Iced Coffee Maker: was $129 now $99</a></strong><br>This stylish Ninja coffee maker comes in a gorgeous Sage colorway and is now on sale for $30 off. Whether you prefer pods or grounds, this device makes tasty brews in 7 sizes (6-oz. to 24-oz) and has a built-in, fold-away milk frother!<a class="view-deal button" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LNKN4Z" target="_blank" rel="nofollow" data-dimension112="d00a8d38-7f92-11f1-afcb-7d930b89fd78" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="b574e648-7f8e-11f1-b9d4-89a53ea85432" data-action="Deal Block" data-label="FlexBreeze HydroGo" data-dimension48="FlexBreeze HydroGo" data-dimension25="$99" href="https://www.amazon.com/Shark-FlexBreeze-Fan-Ultra-powerful-evaporative-FA052/dp/B0DXRCXX3H?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:972px;"><p class="vanilla-image-block" style="padding-top:107.72%;"><img id="x9jNDzwJFNFjGUd4m8cbMN" name="Shark FlexBreeze TableMate.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/x9jNDzwJFNFjGUd4m8cbMN.jpg" mos="" align="middle" fullscreen="" width="972" height="1047" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The FlexBreeze HydroGo is reduced by 34% at Amazon right now. It dispenses water from a very fine misting mechanism which sits right at the center of the fan. And it's portable, which means you can bring it anywhere. It's available in additional colors but the white features the deepest discount of $50. <a class="view-deal button" href="https://www.amazon.com/Shark-FlexBreeze-Fan-Ultra-powerful-evaporative-FA052/dp/B0DXRCXX3H?" target="_blank" rel="nofollow" data-dimension112="b574e648-7f8e-11f1-b9d4-89a53ea85432" data-action="Deal Block" data-label="FlexBreeze HydroGo" data-dimension48="FlexBreeze HydroGo" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="b574e6ac-7f8e-11f1-b33e-ddc27ee0fff6" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$119" href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:522px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="pKMqdL3azKMYSNwdTHk4m9" name="Ninja Never Clog" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/pKMqdL3azKMYSNwdTHk4m9.jpg" mos="" align="middle" fullscreen="" width="522" height="522" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow" data-dimension112="b574e6ac-7f8e-11f1-b33e-ddc27ee0fff6" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$119">NeverClog Cold Press Juicer: was $149 now $119</a></strong><br>Create healthy juices from the comforts of your home with the Ninja Cold Press Juicer Pro. It features a powerful high torque motor and includes two one-touch programs that make it easy to operate. We also like that it has a pulp control that allows you to customize your juice with pulp or without.<a class="view-deal button" href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow" data-dimension112="b574e6ac-7f8e-11f1-b33e-ddc27ee0fff6" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$119">View Deal</a></p></div><div class="product"><a data-dimension112="b574e71a-7f8e-11f1-abc0-19426941cef5" data-action="Deal Block" data-label="StainForce Cordless Spot Cleaner" data-dimension48="StainForce Cordless Spot Cleaner" data-dimension25="$139" href="https://www.amazon.com/dp/B0GVPWP2C3?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3963px;"><p class="vanilla-image-block" style="padding-top:100.03%;"><img id="9Lvm4YeRWmAuHgwxD7KaUj" name="Shark StainForce" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/9Lvm4YeRWmAuHgwxD7KaUj.jpg" mos="" align="middle" fullscreen="" width="3963" height="3964" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0GVPWP2C3?" target="_blank" rel="nofollow" data-dimension112="b574e71a-7f8e-11f1-abc0-19426941cef5" data-action="Deal Block" data-label="StainForce Cordless Spot Cleaner" data-dimension48="StainForce Cordless Spot Cleaner" data-dimension25="$139">StainForce Cordless Spot Cleaner: was $169 now $139</a></strong><br>The Shark StainForce is a compact and cordless spot cleaner, with ultra powerful suction and stain-fighting power. Used alongside the accompanying StainForce cleaner, it eliminates both fresh and set-in stains. Plus it can be emptied quickly, so you're ready for the next challenge.<a class="view-deal button" href="https://www.amazon.com/dp/B0GVPWP2C3?" target="_blank" rel="nofollow" data-dimension112="b574e71a-7f8e-11f1-abc0-19426941cef5" data-action="Deal Block" data-label="StainForce Cordless Spot Cleaner" data-dimension48="StainForce Cordless Spot Cleaner" data-dimension25="$139">View Deal</a></p></div><div class="product"><a data-dimension112="b574e77e-7f8e-11f1-a33b-813c3dfa4b2f" data-action="Deal Block" data-label="HyperHeat 6.5-qt Pressure Cooker" data-dimension48="HyperHeat 6.5-qt Pressure Cooker" data-dimension25="$139" href="https://www.amazon.com/dp/B0FJ9DY1RV?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:505px;"><p class="vanilla-image-block" style="padding-top:99.80%;"><img id="LEhs6pbk66mbtxwH2pH5aV" name="Ninja HyperHeat 6.5 qt Pressure Cooker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/LEhs6pbk66mbtxwH2pH5aV.jpg" mos="" align="middle" fullscreen="" width="505" height="504" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0FJ9DY1RV?" target="_blank" rel="nofollow" data-dimension112="b574e77e-7f8e-11f1-a33b-813c3dfa4b2f" data-action="Deal Block" data-label="HyperHeat 6.5-qt Pressure Cooker" data-dimension48="HyperHeat 6.5-qt Pressure Cooker" data-dimension25="$139">HyperHeat 6.5-qt Pressure Cooker: was $169 now $139</a></strong><br>Ninja's latest pressure cooker cooks up to 2x faster than traditional slow cookers and offers nine functions. The extra-large capacity can feed up to 10 people and fit a 5lb roast. And for easy cleaning, it features a removable aluminum inner pot with a nonstick ceramic coating.<a class="view-deal button" href="https://www.amazon.com/dp/B0FJ9DY1RV?" target="_blank" rel="nofollow" data-dimension112="b574e77e-7f8e-11f1-a33b-813c3dfa4b2f" data-action="Deal Block" data-label="HyperHeat 6.5-qt Pressure Cooker" data-dimension48="HyperHeat 6.5-qt Pressure Cooker" data-dimension25="$139">View Deal</a></p></div><div class="product"><a data-dimension112="d00a8de2-7f92-11f1-85e5-0fedbae9e41d" data-action="Deal Block" data-label="NeverChange Air Purifier" data-dimension48="NeverChange Air Purifier" data-dimension25="$148" href="https://www.amazon.com/dp/B0DSGF8695?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="HsrxRPDwoVoemqsXqX4g8D" name="shark-air-purifier-for-home-neverchange--7b013e69-1737-4edc-85eb-c2c141ea6189.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/HsrxRPDwoVoemqsXqX4g8D.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0DSGF8695?" target="_blank" rel="nofollow" data-dimension112="d00a8de2-7f92-11f1-85e5-0fedbae9e41d" data-action="Deal Block" data-label="NeverChange Air Purifier" data-dimension48="NeverChange Air Purifier" data-dimension25="$148">NeverChange Air Purifier: was $179 now $148</a></strong><br>The highly-rated <a href="https://www.tomsguide.com/home/air-purifiers/shark-neverchange-air-purifier-compact-pro-review">Shark NeverChange Compact Pro</a> air purifier ticks all our boxes, in both looks and functionality. Arguably, the best thing about it has got to be its washable filter — this claims to last for at least 5 years before it'll need replacing. We rank it as the <a href="https://www.tomsguide.com/best-picks/best-air-purifier">best air purifier</a> for small spaces.<a class="view-deal button" href="https://www.amazon.com/dp/B0DSGF8695?" target="_blank" rel="nofollow" data-dimension112="d00a8de2-7f92-11f1-85e5-0fedbae9e41d" data-action="Deal Block" data-label="NeverChange Air Purifier" data-dimension48="NeverChange Air Purifier" data-dimension25="$148">View Deal</a></p></div><div class="product"><a data-dimension112="b574e7e2-7f8e-11f1-b51a-8f8e4f282353" data-action="Deal Block" data-label="ChillPill" data-dimension48="ChillPill" data-dimension25="$149" href="https://www.amazon.com/NEW-Shark-ChillPill-InstaChill-Rechargeable/dp/B0GGTK6RXN" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:911px;"><p class="vanilla-image-block" style="padding-top:164.65%;"><img id="QV9RbWbcgW4KvhYwrmb8kb" name="Shark ChillPill" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/QV9RbWbcgW4KvhYwrmb8kb.jpg" mos="" align="middle" fullscreen="" width="911" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This isn't on sale, but it's a must-have for hot days on campus or in the dorms. The ChillPill is Shark's latest innovation in handheld personal cooling. It comes with three attachments, including a fan cap, misting pod, and InstaChill cooling plate for every need.  It also comes with a digital LCD screen and up to 11 hours of battery life. If you're hoping to keep cool this summer, this could be the fan you've been looking for. <a class="view-deal button" href="https://www.amazon.com/NEW-Shark-ChillPill-InstaChill-Rechargeable/dp/B0GGTK6RXN" target="_blank" rel="nofollow" data-dimension112="b574e7e2-7f8e-11f1-b51a-8f8e4f282353" data-action="Deal Block" data-label="ChillPill" data-dimension48="ChillPill" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b574e846-7f8e-11f1-878c-354f0c170169" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$149" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1676px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="sWibiMn9jcqpLxyQRazy6f" name="Ninja Amazon 2.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sWibiMn9jcqpLxyQRazy6f.jpg" mos="" align="middle" fullscreen="" width="1676" height="1676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="b574e846-7f8e-11f1-878c-354f0c170169" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$149">Mega Kitchen System: was $219 now $149</a></strong><br>Leave all the multi-tasking in the kitchen to one appliance with this Ninja blender. With four functions for smoothies, processing, dough, drinks and more it will quickly become your go-to kitchen appliance. With this ultimate appliance, you’ll be enjoying fresh, healthy drinks, homemade bread and sweet bakes.<a class="view-deal button" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="b574e846-7f8e-11f1-878c-354f0c170169" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b574e8aa-7f8e-11f1-b99a-4d77495ac17c" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:85.86%;"><img id="zNkXNCVYCubXhCRwjaoGPj" name="Ninjs Crispi" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zNkXNCVYCubXhCRwjaoGPj.jpg" mos="" align="middle" fullscreen="" width="679" height="583" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="b574e8aa-7f8e-11f1-b99a-4d77495ac17c" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">Crispi 4-in-1 Portable Glass Air Fryer: was $179 now $159</a></strong><br>The Ninja Crispi is one of Ninja's most interesting recent releases. It features a removable cooking lid and interchangeable glass containers to cook meals of a range of sizes. In our <a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">Ninja Crispi review</a>, it earned a well-deserved 4.5 stars.  <a class="view-deal button" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="b574e8aa-7f8e-11f1-b99a-4d77495ac17c" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">View Deal</a></p></div><div class="product"><a data-dimension112="b574e918-7f8e-11f1-93f5-1133f1de1268" data-action="Deal Block" data-label="FlexStyle" data-dimension48="FlexStyle" data-dimension25="$259" href="https://www.amazon.com/dp/B0BBSFTJG5?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2096px;"><p class="vanilla-image-block" style="padding-top:74.62%;"><img id="67jrCEVq43FBh2WvzZBvXX" name="Shark FlexStyle" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/67jrCEVq43FBh2WvzZBvXX.png" mos="" align="middle" fullscreen="" width="2096" height="1564" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Shark FlexStyle replicates salon-quality results at home. Designed for all hair types, it includes multiple attachments that handle everything from blowouts and waves, to straightening and more. I've been using this Shark hair tool for a few months now, and I can honestly say my hair has never looked better. What used to take me an hour of blow-drying and styling now takes just 20 minutes — even with my sometimes stubborn hair.<a class="view-deal button" href="https://www.amazon.com/dp/B0BBSFTJG5?" target="_blank" rel="nofollow" data-dimension112="b574e918-7f8e-11f1-93f5-1133f1de1268" data-action="Deal Block" data-label="FlexStyle" data-dimension48="FlexStyle" data-dimension25="$259">View Deal</a></p></div><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Home+Appliances&brands=Ninja%2CShark&min_discount_ratio=0.95&offer_type=all&view_mode=carousel&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=light_blue" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/LCxBaXIgdGFncywsLCwsLCwsLCwsLCwsDQosLFBvcnRhYmxlIGNoYXJnZXJzL3dpcmVsZXNzIGNoYXJnZXJzLCwsLCwsLCwsLCwsLCwNCiwsIldhdGVyIGJvdHRsZXMgKHN0YW5sZXlzLCBPd2FsYSwgSHlkcm8gZmxhc2ssIFlldGkpIiwsLCwsLCwsLCwsLCwsDQosLEhhbmQgaGVsZCBmYW5zLCwsLCwsLCwsLCwsLCwNCiwsLCwsLCwsLCwsLCwsLCwNCmhvbWUgb2ZmaWNlLCxvZmZpY2UgY2hhaXJzLCwsLCwsLCwsLCwsLCwNCiwsc3RhbmRpbmcgZGVza3MsLCwsLCwsLCwsLCwsLA0KLCxtb25pdG9ycywsLCwsLCwsLCwsLCwsDQosLEtleWJvYXJkcywsLCwsLCwsLCwsLCwsDQosLGRvY2tpbmcgc3RhdGlvbiwsLCwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpHYW1pbmcsLENvbnNvbGVzLCwsLCwsLCwsLCwsLCwNCiwsQWNjZXNzb3JpZXMsLCwsLCwsLCwsLCwsLA0KLCxHYW1lcywsLCwsLCwsLCwsLCwsDQosLENvdWxkIGluY2x1ZGUgTGVnbz8sLCwsLCwsLCwsLCwsLA==")));              const text = preloadedCSV;              const parsed = parseCSV(text);                            const rowsByName = {};              let filterStart = -1;              parsed.forEach((rc, i) => {                 if (rc && rc.length > 0 && rc[0]) rowsByName[rc[0]] = rc;                 if (rc && rc.length > 0 && rc[0] === 'Filter buttons') filterStart = i;              });                            const cols = [];              if(rowsByName['Roundel text']) {                const headerRow = rowsByName['Roundel text'];                for(let col = 1; col < headerRow.length; col++) {                   let label = headerRow[col];                   if (!label) continue;                                      let q = rowsByName['Search Query'] && rowsByName['Search Query'][col] ? rowsByName['Search Query'][col] : '';                   let img = rowsByName['Roundel image'] && rowsByName['Roundel image'][col] ? rowsByName['Roundel image'][col] : '';                   let ds = rowsByName['Discount Amount'] && rowsByName['Discount Amount'][col] ? rowsByName['Discount Amount'][col] : '';                   let pr = rowsByName['Price Range'] && rowsByName['Price Range'][col] ? rowsByName['Price Range'][col] : '';                   let rt = rowsByName['Retailer'] && rowsByName['Retailer'][col] ? rowsByName['Retailer'][col] : '';                   let ot = rowsByName['Offer Type'] && rowsByName['Offer Type'][col] ? rowsByName['Offer Type'][col] : '';                                      let filters = [];                   if(filterStart > 0) {                     for(let r = filterStart + 1; r < parsed.length; r++) {                         if(!parsed[r] || parsed[r][0] === 'Notes' || parsed[r][0] === 'Categories to consider') break;                         let f = parsed[r][col];                         if(f) filters.push(f);                     }                   }                   cols.push({ label, img, q, ds, pr, rt, ot, filters });                }              }              this.carouselData = cols;              if (this.carouselData && this.carouselData.length > 0) {                 const isMatched = this.carouselData.some(c => c.q === this.currentQuery || c.label === this.currentQuery);                 if (!isMatched) {                    const first = this.carouselData[0];                    this.currentQuery = first.q || first.label;                    if (this.priceFilter) this.priceFilter.value = 'all';                    if (this.customPriceMin) this.customPriceMin.value = '';                    if (this.customPriceMax) this.customPriceMax.value = '';                    let dPr = first.pr || 'all';                    if (typeof dPr === 'string' && dPr !== 'all') {                       let prLower = dPr.toLowerCase();                       if (prLower.includes('min') || prLower.includes('over')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else if (prLower.includes('max') || prLower.includes('under')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       }                    }                    let dAm = '0';                    if(first.ds && typeof first.ds === 'string') {                       let m = first.ds.match(/(\d+)/);                       if(m) dAm = m[1];                    }                    if (this.discountFilter) this.discountFilter.value = dAm;                    if (this.offerTypeSelect) this.offerTypeSelect.value = first.ot || '';                    if (this.retailerSelect) this.retailerSelect.value = first.rt || '';                    this.selectedBrands = [];                    if (this.brandDropdown) {                        const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                        chks.forEach(chk => chk.checked = false);                    }                    if (this.searchInput) this.searchInput.value = this.currentQuery;                 }              }              this.renderCarouselUI();          } catch(e){ console.warn(e); }        }                renderCarouselUI() {           const roundelWrapper = this.root.querySelector('.tg-df-carousel-roundels');           if(!roundelWrapper || !this.carouselData) return;                      let html = '';           this.carouselData.forEach(r => {              const q = r.q || r.label;              const isActive = (this.currentQuery === q || this.currentQuery === r.label) ? 'active' : '';              const imgHtml = r.img ? `\x3Cimg src="${r.img}" alt="${r.label}" />` : `\x3Csvg width="32" height="32" fill="#1F69FF" viewBox="0 0 24 24">\x3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\x3C/svg>`;              html += `                \x3Cdiv class="tg-df-roundel tg-df-carousel-cat ${isActive}" data-label="${this.escapeHTML(r.label)}">                  \x3Cdiv class="tg-df-roundel-img-box">${imgHtml}\x3C/div>                  \x3Cspan class="tg-df-roundel-label">${this.escapeHTML(r.label)}\x3C/span>                \x3C/div>              `;           });           roundelWrapper.innerHTML = html;                      // Rebind clicks           const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');           roundels.forEach(rNode => {             rNode.addEventListener('click', () => {                const r = this.carouselData.find(c => c.label === rNode.getAttribute('data-label'));                 if(!r) return;                                  if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: r.label,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = r.q || r.label;                const labelTitle = this.root.querySelector('#tg-df-carousel-title-label');                if (labelTitle) labelTitle.textContent = 'Best ' + this.currentQuery;                if (this.priceFilter) this.priceFilter.value = 'all';                if (this.customPriceMin) this.customPriceMin.value = '';                if (this.customPriceMax) this.customPriceMax.value = '';                let dPr = r.pr || 'all';                if (typeof dPr === 'string' && dPr !== 'all') {                   let prLower = dPr.toLowerCase();                   if (prLower.includes('min') || prLower.includes('over')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMin) this.customPriceMin.value = m[1];                   } else if (prLower.includes('max') || prLower.includes('under')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMax) this.customPriceMax.value = m[1];                   }                }                                let discountAmount = '0';                if(r.ds && typeof r.ds === 'string') {                   let m = r.ds.match(/(\d+)/);                   if(m) discountAmount = m[1];                }                if (this.discountFilter) this.discountFilter.value = discountAmount;                if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                                // Clear brands                    this.selectedBrands = [];                    if (this.brandDropdown) {                    const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                    chks.forEach(chk => chk.checked = false);                }                                if (this.searchInput) this.searchInput.value = this.currentQuery;                                roundels.forEach(ro => ro.classList.remove('active'));                if (rNode) rNode.classList.add('active');                                this.renderCarouselFilters(r);                this.fetchDeals(this.currentQuery);             });           });                      // Auto-highlight active           const activeR = this.carouselData.find(c => c.q === this.currentQuery || c.label === this.currentQuery);           if(activeR) this.renderCarouselFilters(activeR);        }                renderCarouselFilters(r) {           const filtersWrap = this.root.querySelector('.tg-df-carousel-filters-wrap');           if(!filtersWrap) return;                      let html = `\x3Cbutton class="tg-df-carousel-filter-btn" data-type="all">All\x3C/button>`;                      r.filters.forEach(f => {              let fL = f.toLowerCase();              let icon = '';              let logic = `data-type="custom" data-v="${this.escapeHTML(f)}"`;              if (fL === 'amazon deals' || fL === 'prime deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>`;              } else if (fL === 'lightning deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>`;              } else {                 if (fL.includes('lightning')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap">\x3Cpolygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2">\x3C/polygon>\x3C/svg>`;                 } else if (fL.includes('% off')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>`;                 } else if (fL.includes('under') || fL.includes('min ')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>`;                 }                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>${icon} ${this.escapeHTML(f)}\x3C/button>`;              }           });                      filtersWrap.innerHTML = html;                      const btns = filtersWrap.querySelectorAll('button');           btns.forEach(b => {             b.addEventListener('click', () => {                const type = b.getAttribute('data-type');                if (type === 'custom') {                   const v = b.getAttribute('data-v');                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-custom-${(v||'').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`, name: 'Custom Filter', label: v });                }                if (type === 'all') {                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: 'filter-clear-all', name: 'Clear all', label: 'Clear all filters' });                   // reset everything                   btns.forEach(btn => btn.classList.remove('active'));                   b.classList.add('active');                                      // Reset prices                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   if (this.brandDropdown) {                     const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                     chks.forEach(chk => chk.checked = false);                   }                } else {                   const v = b.getAttribute('data-v');                   const fL = v.toLowerCase();                                      let mapRet = ['amazon', 'walmart', 'best buy', 'target', 'john lewis', 'currys', 'argos'];                   const getCategory = (s) => {                      if (s === 'lightning deals' || s === 'amazon deals' || s === 'prime deals') return 'offer';                      if (s.includes('% off')) return 'discount';                      if (s.includes('under') || s.includes('over') || s.includes('min') || s.includes('max')) return 'price';                      if (mapRet.includes(s)) return 'retailer';                      return 'brand';                   };                   const cat = getCategory(fL);                   const wasActive = b.classList.contains('active');                   if (cat !== 'brand') {                      btns.forEach(btn => {                          if (btn === b) return;                          if (btn.getAttribute('data-type') === 'all') return;                          const bV = btn.getAttribute('data-v');                          if (!bV) return;                          if (getCategory(bV.toLowerCase()) === cat) btn.classList.remove('active');                      });                   }                   if (wasActive) b.classList.remove('active');                   else b.classList.add('active');                   let anyActive = Array.from(btns).some(btn => btn !== btns[0] && btn.classList.contains('active'));                   if (!anyActive) {                       btns[0].click();                       return;                   } else {                       btns[0].classList.remove('active');                   }                                      if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   btns.forEach(btn => {                       if (!btn.classList.contains('active') || btn.getAttribute('data-type') === 'all') return;                       const vv = btn.getAttribute('data-v');                       const vl = vv.toLowerCase();                                              if (vl === 'lightning deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_lightning';                       } else if (vl === 'amazon deals' || vl === 'prime deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_prime';                       } else if (vl.includes('% off')) {                          let m = vl.match(/(\d+)%/);                          if (m && this.discountFilter) this.discountFilter.value = m[1];                       } else if (vl.includes('under') || vl.includes('max')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       } else if (vl.includes('min') || vl.includes('over')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else {                          let foundR = mapRet.find(x => x === vl);                          if (foundR) {                             let realR = ['Amazon', 'Walmart', 'Best Buy', 'Target', 'John Lewis', 'Currys', 'Argos'].find(x => x.toLowerCase() === vl);                             if (this.retailerSelect) this.retailerSelect.value = realR;                          } else {                             this.selectedBrands.push(vv);                          }                       }                   });                                      if (this.brandDropdown) {                       const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                       chks.forEach(c => c.checked = this.selectedBrands.includes(c.value));                   }                                      if (r.pr && typeof r.pr === 'string') {                       let prL = r.pr.toLowerCase();                       if (prL.includes('under $')) {                           let m = prL.match(/under \$(\d+)/i);                           if (m && this.customPriceMax && !this.customPriceMax.value) this.customPriceMax.value = m[1];                       }                   }                }                                this.fetchDeals(this.currentQuery);             });           });                      // default to highlighting first           btns[0].classList.add('active');        }async fetchDeals(query, append = false) {          if (!append) {             this.showLoading();             this.deals = [];             this.displayLimit = this.getRowsLimit();          } else {             this.displayLimit += this.getRowsLimit();          }                    try {            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad(append);            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query, append);               } else {                 await this.fetchHawkDeals(query, append);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query, append);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const url = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let dynamicBrandsCounts = {};                    targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                   dynamicBrandsCounts[data.brand] = (dynamicBrandsCounts[data.brand] || 0) + 1;                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || ''                });             });          });                    const airedaleBrandsList = Object.keys(dynamicBrandsCounts).map(b => ({              formatted_value: b,              count: dynamicBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query, append = false) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (append && this.deals.length > 0) {            url.searchParams.append('offset', this.deals.length.toString());          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : (this.getViewMode() === 'savings_squad' ? 'date_desc' : 'discount_desc');          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin-top:8px;">${_div}                ${_div}              ${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)} (${this.airedaleTagCounts[tag] || 0})${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = isSavingsSquadMode ? 'View Deal' : 'Check Price';              if (isSavingsSquadMode) {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                  ${_div}                `;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = `                \x3Cdiv class="tg-df-card-merchant-wrapper">                  \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                ${_div}                \x3Cdiv class="tg-df-card-price-group">                  ${isSavingsSquadMode ? '' : `                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                  `}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description">\x3Cp>${this.escapeHTML(deal.description)}${_p}${_div}` : ''}                      ${_div}                    ${_div}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>${deal.isCheckPrice ? 'Check Price' : 'View Deal'}${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cp style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 12px; line-height: 1.4;">${this.escapeHTML(deal.description)}${_p}` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${priceGroupHtml}                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta ${isSavingsSquadMode ? 'tg-df-cta-savings-squad' : ''}" style="text-decoration: none;">${ctaText}${_a}                  ${_div}                ${_div}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit || ((this.getViewMode() === 'carousel' || this.getViewMode() === 'auto') && displayDeals.length > 0 && displayDeals.length % ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12) === 0)) {            if (this.getViewMode() === 'carousel') {               dealsHtml += `                 \x3Cbutton type="button" class="tg-df-load-more-card tg-df-load-more">                   \x3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;">\x3Cpath d="M5 12h14">\x3C/path>\x3Cpath d="m12 5 7 7-7 7">\x3C/path>\x3C/svg>                   Load More                 ${_button}               `;            } else {               dealsHtml += `                 \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                   \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer; display: flex; align-items: center;">Load More${_button}                 ${_div}               `;            }          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let rightChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             let leftChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-left');             if (this.getViewMode() === 'carousel') {                 // The observer set up in setupScrollListeners handles visibility.                 if (rightChevron) rightChevron.style.display = 'flex';                 if (leftChevron) leftChevron.style.display = 'none'; // reset correctly             } else {                 if (rightChevron) rightChevron.style.display = 'none';                 if (leftChevron) leftChevron.style.display = 'none';             }          }                    const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', async () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              if (displayDeals.length <= this.displayLimit) {                 loadMoreBtn.innerHTML = `                  <svg class="tg-df-spinner" style="width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 8px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/></svg>                  Loading...                 `;                 loadMoreBtn.disabled = true;                 await this.fetchDeals(this.currentQuery, true);              } else {                 this.displayLimit += this.getRowsLimit();                 this.render();              }            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            setTimeout(() => {               if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');               document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));            }, 50);          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/13-shark-and-ninja-deals-thatll-make-your-dorm-feel-more-like-home-back-to-school-appliances-from-usd79</link>
                                                                            <description>
                            <![CDATA[ Amazon is knocking up to 40% off Ninja and Shark appliances ahead of back to school season. Check out my favorite deals on fans, air fryers, vacuums and more. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">JaHRAFMxkjvsPLQwya6LJa</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/eMzwr56nffqBtfmMqVVd7k-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 14 Jul 2026 15:44:19 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                                    <dc:creator><![CDATA[ Olivia Halevy ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/YAg7Fmh9dGKbg3hULBMWiU.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Olivia is a Deals Writer at Tom’s Guide. She loves all things lifestyle, shopping and sourcing the best deals from some of the top brands, retailers and websites around. From fashion and beauty to smart home and tech, she has an eye for finding practical, stylish, and reasonably-priced items that help make life simpler (and a little more aesthetically-pleasing).&amp;nbsp;Olivia has over five years of experience writing lifestyle, commerce, and branded content for &lt;a href=&quot;http://patch.com/&quot;&gt;Patch.com&lt;/a&gt;. She&#039;s covered major shopping holidays like Amazon Prime Day, Black Friday and Cyber Monday. In her free time, she enjoys trying out new exercise classes, binge-watching the latest viral TV shows, cooking, spending time with family and taking long walks with her dog, Gus.&amp;nbsp;&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/eMzwr56nffqBtfmMqVVd7k-1280-80.jpg">
                                                            <media:credit><![CDATA[Amazon / edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Ninja Shark]]></media:description>                                                            <media:text><![CDATA[Ninja Shark]]></media:text>
                                <media:title type="plain"><![CDATA[Ninja Shark]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/eMzwr56nffqBtfmMqVVd7k-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It's been several years since I lived in a dorm — but if I were shopping for back to school season today, I'd be super excited about the latest appliances from Ninja and Shark. From portable smoothie makers and misting fans to cordless vacuums and multifunctional hair tools, these space-saving essentials make dorm life simpler. </p><p>If you're gearing up for the new semester, I recommend <a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow">grabbing some Shark and Ninja appliances</a> — especially right now since Amazon is knocking up to 40% off select models. For instance, <a href="https://www.amazon.com/Shark-FlexBreeze-Fan-Ultra-powerful-evaporative-FA052/dp/B0DXRCXX3H?" target="_blank" rel="nofollow">the Shark FlexBreeze HydroGo is currently over $50 off</a> and is perfect for keep you cool while hanging out, studying or sleeping in the dorm.</p><p>There's plenty more convenient devices for back to school season below — and they start at just $79. Keep scrolling to check out all my favorite Shark and Ninja appliance deals on Amazon.</p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><strong></strong><a href="https://www.sharkninja.com/sales-offers/shop-all-sale" target="_blank" rel="nofollow"><strong>shop all SharkNinja deals</strong></a></li><li><a href="https://www.amazon.com/stores/page/471F718E-0761-48DF-ABAF-0FC6590C695B" target="_blank" rel="nofollow"><strong>shop all SharkNinja deals at Amazon</strong></a></li><li><strong>Shark Steam Pocket Mop: </strong><a href="https://www.amazon.com/Shark-Pocket-Cleaner-Steering-S3501/dp/B0028MB3HM" target="_blank" rel="nofollow"><strong>was $99 now $79</strong></a></li><li><strong>Ninja Blast Max: </strong><a href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow"><strong>was $99 now $79</strong></a><strong></strong></li><li><strong>Ninja Iced Coffee Maker: </strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LNKN4Z" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Shark FlexBreeze HydroGo: </strong><a href="https://www.amazon.com/Shark-FlexBreeze-Fan-Ultra-powerful-evaporative-FA052/dp/B0DXRCXX3H?" target="_blank" rel="nofollow"><strong>was $149 now $99</strong></a></li><li><strong>Ninja Mega Kitchen System: </strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><strong>was $219 now $149</strong></a></li><li><strong>Ninja Crispi 4-in-1 Portable Glass Air Fryer: </strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><strong>was $179 now $159</strong></a></li><li><strong>Shark FlexStyle: </strong><a href="https://www.amazon.com/dp/B0BBSFTJG5?" target="_blank" rel="nofollow"><strong>was $349 now $259</strong></a></li></ul><h3 class="article-body__section" id="section-ninja-shark-deals"><span>Ninja/Shark Deals</span></h3><div class="product"><a data-dimension112="b574e512-7f8e-11f1-a9f7-d1a1f6c7f145" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69" href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1216px;"><p class="vanilla-image-block" style="padding-top:123.36%;"><img id="9Q4QY399FaozAFUt4j8dTW" name="ninja-blast-max-portable-blender--twist--bb517479-54ce-4a77-974c-58e2f44dde33.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/9Q4QY399FaozAFUt4j8dTW.jpg" mos="" align="middle" fullscreen="" width="1216" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Blast Max is capable of taking on any challenge you throw at it, including crushing ice and blitzing berries to make yummy smoothies. It comes with multiple blend settings to make sure your blender is using the right level of power for the task at hand, and the compact size means it’s a great option for anyone with a small kitchen. <a class="view-deal button" href="https://www.amazon.com/dp/B0GX2SD3K6?" target="_blank" rel="nofollow" data-dimension112="b574e512-7f8e-11f1-a9f7-d1a1f6c7f145" data-action="Deal Block" data-label="Blast Max" data-dimension48="Blast Max" data-dimension25="$69">View Deal</a></p></div><div class="product"><a data-dimension112="b574e576-7f8e-11f1-88de-39c75248c5d0" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79" href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1614px;"><p class="vanilla-image-block" style="padding-top:86.00%;"><img id="XofzJVhShmZ3kcVxuocQvB" name="Ninja Belgian Waffle Maker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/XofzJVhShmZ3kcVxuocQvB.png" mos="" align="middle" fullscreen="" width="1614" height="1388" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow" data-dimension112="b574e576-7f8e-11f1-88de-39c75248c5d0" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79">Belgian Waffle Maker: was $99 now $79</a></strong><br>Weekend brunch just got an upgrade. This vertical waffle maker has five shade settings so everyone gets their waffle exactly how they like it, plus a specialty setting for high-sugar batters like brownies (yes, really). The precision-pour cup takes the guesswork out of measuring, and audible alerts mean no more burnt disasters. Pair it with the Ninja CREAMi for the ultimate breakfast-dessert combo. <a class="view-deal button" href="https://www.amazon.com/Ninja-BW1001-NeverStick-Precision-Pour-Chef-curated/dp/B0B358VDZJ/ref=" target="_blank" rel="nofollow" data-dimension112="b574e576-7f8e-11f1-88de-39c75248c5d0" data-action="Deal Block" data-label="Belgian Waffle Maker" data-dimension48="Belgian Waffle Maker" data-dimension25="$79">View Deal</a></p></div><div class="product"><a data-dimension112="b574e5e4-7f8e-11f1-8564-799eca30cc34" data-action="Deal Block" data-label="Steam Pocket Mop Hard Floor Cleaner" data-dimension48="Steam Pocket Mop Hard Floor Cleaner" data-dimension25="$79" href="https://www.amazon.com/Shark-Pocket-Cleaner-Steering-S3501/dp/B0028MB3HM" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:343px;"><p class="vanilla-image-block" style="padding-top:256.27%;"><img id="xuU833BnMdHYwfAXhFZQvf" name="Shark Steam Pocket Mop Hard Floor Cleaner.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/xuU833BnMdHYwfAXhFZQvf.jpg" mos="" align="middle" fullscreen="" width="343" height="879" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Shark-Pocket-Cleaner-Steering-S3501/dp/B0028MB3HM" target="_blank" rel="nofollow" data-dimension112="b574e5e4-7f8e-11f1-8564-799eca30cc34" data-action="Deal Block" data-label="Steam Pocket Mop Hard Floor Cleaner" data-dimension48="Steam Pocket Mop Hard Floor Cleaner" data-dimension25="$79">Steam Pocket Mop Hard Floor Cleaner: was $99 now $79</a></strong><br>With the Shark Steam Pocket Mop, mopping might just be your new favorite household chore. Designed for sealed hard floors like hardwood, marble, tile, stone, laminate and vinyl, the mop transforms water into super-heated steam in seconds to remove dirt, grime, and hard-to-clean messes. This will help you remove up to 99% of household bacteria — without the harsh chemicals. <a class="view-deal button" href="https://www.amazon.com/Shark-Pocket-Cleaner-Steering-S3501/dp/B0028MB3HM" target="_blank" rel="nofollow" data-dimension112="b574e5e4-7f8e-11f1-8564-799eca30cc34" data-action="Deal Block" data-label="Steam Pocket Mop Hard Floor Cleaner" data-dimension48="Steam Pocket Mop Hard Floor Cleaner" data-dimension25="$79">View Deal</a></p></div><div class="product"><a data-dimension112="d00a8d38-7f92-11f1-afcb-7d930b89fd78" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LNKN4Z" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:99.56%;"><img id="4uiaRXRKY8Go6F4ZpM7UdA" name="Ninja coffee" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4uiaRXRKY8Go6F4ZpM7UdA.jpg" mos="" align="middle" fullscreen="" width="679" height="676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LNKN4Z" target="_blank" rel="nofollow" data-dimension112="d00a8d38-7f92-11f1-afcb-7d930b89fd78" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">Pods & Grounds Specialty Single-Serve Iced Coffee Maker: was $129 now $99</a></strong><br>This stylish Ninja coffee maker comes in a gorgeous Sage colorway and is now on sale for $30 off. Whether you prefer pods or grounds, this device makes tasty brews in 7 sizes (6-oz. to 24-oz) and has a built-in, fold-away milk frother!<a class="view-deal button" href="https://www.amazon.com/Ninja-Specialty-Single-Serve-Compatible-PB055ABL/dp/B0D1LNKN4Z" target="_blank" rel="nofollow" data-dimension112="d00a8d38-7f92-11f1-afcb-7d930b89fd78" data-action="Deal Block" data-label="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension48="Pods & Grounds Specialty Single-Serve Iced Coffee Maker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="b574e648-7f8e-11f1-b9d4-89a53ea85432" data-action="Deal Block" data-label="FlexBreeze HydroGo" data-dimension48="FlexBreeze HydroGo" data-dimension25="$99" href="https://www.amazon.com/Shark-FlexBreeze-Fan-Ultra-powerful-evaporative-FA052/dp/B0DXRCXX3H?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:972px;"><p class="vanilla-image-block" style="padding-top:107.72%;"><img id="x9jNDzwJFNFjGUd4m8cbMN" name="Shark FlexBreeze TableMate.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/x9jNDzwJFNFjGUd4m8cbMN.jpg" mos="" align="middle" fullscreen="" width="972" height="1047" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The FlexBreeze HydroGo is reduced by 34% at Amazon right now. It dispenses water from a very fine misting mechanism which sits right at the center of the fan. And it's portable, which means you can bring it anywhere. It's available in additional colors but the white features the deepest discount of $50. <a class="view-deal button" href="https://www.amazon.com/Shark-FlexBreeze-Fan-Ultra-powerful-evaporative-FA052/dp/B0DXRCXX3H?" target="_blank" rel="nofollow" data-dimension112="b574e648-7f8e-11f1-b9d4-89a53ea85432" data-action="Deal Block" data-label="FlexBreeze HydroGo" data-dimension48="FlexBreeze HydroGo" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="b574e6ac-7f8e-11f1-b33e-ddc27ee0fff6" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$119" href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:522px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="pKMqdL3azKMYSNwdTHk4m9" name="Ninja Never Clog" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/pKMqdL3azKMYSNwdTHk4m9.jpg" mos="" align="middle" fullscreen="" width="522" height="522" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow" data-dimension112="b574e6ac-7f8e-11f1-b33e-ddc27ee0fff6" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$119">NeverClog Cold Press Juicer: was $149 now $119</a></strong><br>Create healthy juices from the comforts of your home with the Ninja Cold Press Juicer Pro. It features a powerful high torque motor and includes two one-touch programs that make it easy to operate. We also like that it has a pulp control that allows you to customize your juice with pulp or without.<a class="view-deal button" href="https://www.amazon.com/dp/B0BR8HZL9V?" target="_blank" rel="nofollow" data-dimension112="b574e6ac-7f8e-11f1-b33e-ddc27ee0fff6" data-action="Deal Block" data-label="NeverClog Cold Press Juicer" data-dimension48="NeverClog Cold Press Juicer" data-dimension25="$119">View Deal</a></p></div><div class="product"><a data-dimension112="b574e71a-7f8e-11f1-abc0-19426941cef5" data-action="Deal Block" data-label="StainForce Cordless Spot Cleaner" data-dimension48="StainForce Cordless Spot Cleaner" data-dimension25="$139" href="https://www.amazon.com/dp/B0GVPWP2C3?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3963px;"><p class="vanilla-image-block" style="padding-top:100.03%;"><img id="9Lvm4YeRWmAuHgwxD7KaUj" name="Shark StainForce" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/9Lvm4YeRWmAuHgwxD7KaUj.jpg" mos="" align="middle" fullscreen="" width="3963" height="3964" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0GVPWP2C3?" target="_blank" rel="nofollow" data-dimension112="b574e71a-7f8e-11f1-abc0-19426941cef5" data-action="Deal Block" data-label="StainForce Cordless Spot Cleaner" data-dimension48="StainForce Cordless Spot Cleaner" data-dimension25="$139">StainForce Cordless Spot Cleaner: was $169 now $139</a></strong><br>The Shark StainForce is a compact and cordless spot cleaner, with ultra powerful suction and stain-fighting power. Used alongside the accompanying StainForce cleaner, it eliminates both fresh and set-in stains. Plus it can be emptied quickly, so you're ready for the next challenge.<a class="view-deal button" href="https://www.amazon.com/dp/B0GVPWP2C3?" target="_blank" rel="nofollow" data-dimension112="b574e71a-7f8e-11f1-abc0-19426941cef5" data-action="Deal Block" data-label="StainForce Cordless Spot Cleaner" data-dimension48="StainForce Cordless Spot Cleaner" data-dimension25="$139">View Deal</a></p></div><div class="product"><a data-dimension112="b574e77e-7f8e-11f1-a33b-813c3dfa4b2f" data-action="Deal Block" data-label="HyperHeat 6.5-qt Pressure Cooker" data-dimension48="HyperHeat 6.5-qt Pressure Cooker" data-dimension25="$139" href="https://www.amazon.com/dp/B0FJ9DY1RV?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:505px;"><p class="vanilla-image-block" style="padding-top:99.80%;"><img id="LEhs6pbk66mbtxwH2pH5aV" name="Ninja HyperHeat 6.5 qt Pressure Cooker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/LEhs6pbk66mbtxwH2pH5aV.jpg" mos="" align="middle" fullscreen="" width="505" height="504" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0FJ9DY1RV?" target="_blank" rel="nofollow" data-dimension112="b574e77e-7f8e-11f1-a33b-813c3dfa4b2f" data-action="Deal Block" data-label="HyperHeat 6.5-qt Pressure Cooker" data-dimension48="HyperHeat 6.5-qt Pressure Cooker" data-dimension25="$139">HyperHeat 6.5-qt Pressure Cooker: was $169 now $139</a></strong><br>Ninja's latest pressure cooker cooks up to 2x faster than traditional slow cookers and offers nine functions. The extra-large capacity can feed up to 10 people and fit a 5lb roast. And for easy cleaning, it features a removable aluminum inner pot with a nonstick ceramic coating.<a class="view-deal button" href="https://www.amazon.com/dp/B0FJ9DY1RV?" target="_blank" rel="nofollow" data-dimension112="b574e77e-7f8e-11f1-a33b-813c3dfa4b2f" data-action="Deal Block" data-label="HyperHeat 6.5-qt Pressure Cooker" data-dimension48="HyperHeat 6.5-qt Pressure Cooker" data-dimension25="$139">View Deal</a></p></div><div class="product"><a data-dimension112="d00a8de2-7f92-11f1-85e5-0fedbae9e41d" data-action="Deal Block" data-label="NeverChange Air Purifier" data-dimension48="NeverChange Air Purifier" data-dimension25="$148" href="https://www.amazon.com/dp/B0DSGF8695?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="HsrxRPDwoVoemqsXqX4g8D" name="shark-air-purifier-for-home-neverchange--7b013e69-1737-4edc-85eb-c2c141ea6189.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/HsrxRPDwoVoemqsXqX4g8D.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0DSGF8695?" target="_blank" rel="nofollow" data-dimension112="d00a8de2-7f92-11f1-85e5-0fedbae9e41d" data-action="Deal Block" data-label="NeverChange Air Purifier" data-dimension48="NeverChange Air Purifier" data-dimension25="$148">NeverChange Air Purifier: was $179 now $148</a></strong><br>The highly-rated <a href="https://www.tomsguide.com/home/air-purifiers/shark-neverchange-air-purifier-compact-pro-review">Shark NeverChange Compact Pro</a> air purifier ticks all our boxes, in both looks and functionality. Arguably, the best thing about it has got to be its washable filter — this claims to last for at least 5 years before it'll need replacing. We rank it as the <a href="https://www.tomsguide.com/best-picks/best-air-purifier">best air purifier</a> for small spaces.<a class="view-deal button" href="https://www.amazon.com/dp/B0DSGF8695?" target="_blank" rel="nofollow" data-dimension112="d00a8de2-7f92-11f1-85e5-0fedbae9e41d" data-action="Deal Block" data-label="NeverChange Air Purifier" data-dimension48="NeverChange Air Purifier" data-dimension25="$148">View Deal</a></p></div><div class="product"><a data-dimension112="b574e7e2-7f8e-11f1-b51a-8f8e4f282353" data-action="Deal Block" data-label="ChillPill" data-dimension48="ChillPill" data-dimension25="$149" href="https://www.amazon.com/NEW-Shark-ChillPill-InstaChill-Rechargeable/dp/B0GGTK6RXN" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:911px;"><p class="vanilla-image-block" style="padding-top:164.65%;"><img id="QV9RbWbcgW4KvhYwrmb8kb" name="Shark ChillPill" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/QV9RbWbcgW4KvhYwrmb8kb.jpg" mos="" align="middle" fullscreen="" width="911" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This isn't on sale, but it's a must-have for hot days on campus or in the dorms. The ChillPill is Shark's latest innovation in handheld personal cooling. It comes with three attachments, including a fan cap, misting pod, and InstaChill cooling plate for every need.  It also comes with a digital LCD screen and up to 11 hours of battery life. If you're hoping to keep cool this summer, this could be the fan you've been looking for. <a class="view-deal button" href="https://www.amazon.com/NEW-Shark-ChillPill-InstaChill-Rechargeable/dp/B0GGTK6RXN" target="_blank" rel="nofollow" data-dimension112="b574e7e2-7f8e-11f1-b51a-8f8e4f282353" data-action="Deal Block" data-label="ChillPill" data-dimension48="ChillPill" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b574e846-7f8e-11f1-878c-354f0c170169" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$149" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1676px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="sWibiMn9jcqpLxyQRazy6f" name="Ninja Amazon 2.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/sWibiMn9jcqpLxyQRazy6f.jpg" mos="" align="middle" fullscreen="" width="1676" height="1676" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="b574e846-7f8e-11f1-878c-354f0c170169" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$149">Mega Kitchen System: was $219 now $149</a></strong><br>Leave all the multi-tasking in the kitchen to one appliance with this Ninja blender. With four functions for smoothies, processing, dough, drinks and more it will quickly become your go-to kitchen appliance. With this ultimate appliance, you’ll be enjoying fresh, healthy drinks, homemade bread and sweet bakes.<a class="view-deal button" href="https://www.amazon.com/dp/B00939I7EK" target="_blank" rel="nofollow" data-dimension112="b574e846-7f8e-11f1-878c-354f0c170169" data-action="Deal Block" data-label="Mega Kitchen System" data-dimension48="Mega Kitchen System" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b574e8aa-7f8e-11f1-b99a-4d77495ac17c" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:85.86%;"><img id="zNkXNCVYCubXhCRwjaoGPj" name="Ninjs Crispi" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zNkXNCVYCubXhCRwjaoGPj.jpg" mos="" align="middle" fullscreen="" width="679" height="583" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="b574e8aa-7f8e-11f1-b99a-4d77495ac17c" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">Crispi 4-in-1 Portable Glass Air Fryer: was $179 now $159</a></strong><br>The Ninja Crispi is one of Ninja's most interesting recent releases. It features a removable cooking lid and interchangeable glass containers to cook meals of a range of sizes. In our <a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">Ninja Crispi review</a>, it earned a well-deserved 4.5 stars.  <a class="view-deal button" href="https://www.amazon.com/Ninja-Microwave-Dishwasher-Containers-FN101GY/dp/B0DDDD8WD6?" target="_blank" rel="nofollow" data-dimension112="b574e8aa-7f8e-11f1-b99a-4d77495ac17c" data-action="Deal Block" data-label="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension48="Crispi 4-in-1 Portable Glass Air Fryer" data-dimension25="$159">View Deal</a></p></div><div class="product"><a data-dimension112="b574e918-7f8e-11f1-93f5-1133f1de1268" data-action="Deal Block" data-label="FlexStyle" data-dimension48="FlexStyle" data-dimension25="$259" href="https://www.amazon.com/dp/B0BBSFTJG5?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2096px;"><p class="vanilla-image-block" style="padding-top:74.62%;"><img id="67jrCEVq43FBh2WvzZBvXX" name="Shark FlexStyle" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/67jrCEVq43FBh2WvzZBvXX.png" mos="" align="middle" fullscreen="" width="2096" height="1564" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Shark FlexStyle replicates salon-quality results at home. Designed for all hair types, it includes multiple attachments that handle everything from blowouts and waves, to straightening and more. I've been using this Shark hair tool for a few months now, and I can honestly say my hair has never looked better. What used to take me an hour of blow-drying and styling now takes just 20 minutes — even with my sometimes stubborn hair.<a class="view-deal button" href="https://www.amazon.com/dp/B0BBSFTJG5?" target="_blank" rel="nofollow" data-dimension112="b574e918-7f8e-11f1-93f5-1133f1de1268" data-action="Deal Block" data-label="FlexStyle" data-dimension48="FlexStyle" data-dimension25="$259">View Deal</a></p></div><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Home+Appliances&brands=Ninja%2CShark&min_discount_ratio=0.95&offer_type=all&view_mode=carousel&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=light_blue" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/LCxBaXIgdGFncywsLCwsLCwsLCwsLCwsDQosLFBvcnRhYmxlIGNoYXJnZXJzL3dpcmVsZXNzIGNoYXJnZXJzLCwsLCwsLCwsLCwsLCwNCiwsIldhdGVyIGJvdHRsZXMgKHN0YW5sZXlzLCBPd2FsYSwgSHlkcm8gZmxhc2ssIFlldGkpIiwsLCwsLCwsLCwsLCwsDQosLEhhbmQgaGVsZCBmYW5zLCwsLCwsLCwsLCwsLCwNCiwsLCwsLCwsLCwsLCwsLCwNCmhvbWUgb2ZmaWNlLCxvZmZpY2UgY2hhaXJzLCwsLCwsLCwsLCwsLCwNCiwsc3RhbmRpbmcgZGVza3MsLCwsLCwsLCwsLCwsLA0KLCxtb25pdG9ycywsLCwsLCwsLCwsLCwsDQosLEtleWJvYXJkcywsLCwsLCwsLCwsLCwsDQosLGRvY2tpbmcgc3RhdGlvbiwsLCwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpHYW1pbmcsLENvbnNvbGVzLCwsLCwsLCwsLCwsLCwNCiwsQWNjZXNzb3JpZXMsLCwsLCwsLCwsLCwsLA0KLCxHYW1lcywsLCwsLCwsLCwsLCwsDQosLENvdWxkIGluY2x1ZGUgTGVnbz8sLCwsLCwsLCwsLCwsLA==")));              const text = preloadedCSV;              const parsed = parseCSV(text);                            const rowsByName = {};              let filterStart = -1;              parsed.forEach((rc, i) => {                 if (rc && rc.length > 0 && rc[0]) rowsByName[rc[0]] = rc;                 if (rc && rc.length > 0 && rc[0] === 'Filter buttons') filterStart = i;              });                            const cols = [];              if(rowsByName['Roundel text']) {                const headerRow = rowsByName['Roundel text'];                for(let col = 1; col < headerRow.length; col++) {                   let label = headerRow[col];                   if (!label) continue;                                      let q = rowsByName['Search Query'] && rowsByName['Search Query'][col] ? rowsByName['Search Query'][col] : '';                   let img = rowsByName['Roundel image'] && rowsByName['Roundel image'][col] ? rowsByName['Roundel image'][col] : '';                   let ds = rowsByName['Discount Amount'] && rowsByName['Discount Amount'][col] ? rowsByName['Discount Amount'][col] : '';                   let pr = rowsByName['Price Range'] && rowsByName['Price Range'][col] ? rowsByName['Price Range'][col] : '';                   let rt = rowsByName['Retailer'] && rowsByName['Retailer'][col] ? rowsByName['Retailer'][col] : '';                   let ot = rowsByName['Offer Type'] && rowsByName['Offer Type'][col] ? rowsByName['Offer Type'][col] : '';                                      let filters = [];                   if(filterStart > 0) {                     for(let r = filterStart + 1; r < parsed.length; r++) {                         if(!parsed[r] || parsed[r][0] === 'Notes' || parsed[r][0] === 'Categories to consider') break;                         let f = parsed[r][col];                         if(f) filters.push(f);                     }                   }                   cols.push({ label, img, q, ds, pr, rt, ot, filters });                }              }              this.carouselData = cols;              if (this.carouselData && this.carouselData.length > 0) {                 const isMatched = this.carouselData.some(c => c.q === this.currentQuery || c.label === this.currentQuery);                 if (!isMatched) {                    const first = this.carouselData[0];                    this.currentQuery = first.q || first.label;                    if (this.priceFilter) this.priceFilter.value = 'all';                    if (this.customPriceMin) this.customPriceMin.value = '';                    if (this.customPriceMax) this.customPriceMax.value = '';                    let dPr = first.pr || 'all';                    if (typeof dPr === 'string' && dPr !== 'all') {                       let prLower = dPr.toLowerCase();                       if (prLower.includes('min') || prLower.includes('over')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else if (prLower.includes('max') || prLower.includes('under')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       }                    }                    let dAm = '0';                    if(first.ds && typeof first.ds === 'string') {                       let m = first.ds.match(/(\d+)/);                       if(m) dAm = m[1];                    }                    if (this.discountFilter) this.discountFilter.value = dAm;                    if (this.offerTypeSelect) this.offerTypeSelect.value = first.ot || '';                    if (this.retailerSelect) this.retailerSelect.value = first.rt || '';                    this.selectedBrands = [];                    if (this.brandDropdown) {                        const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                        chks.forEach(chk => chk.checked = false);                    }                    if (this.searchInput) this.searchInput.value = this.currentQuery;                 }              }              this.renderCarouselUI();          } catch(e){ console.warn(e); }        }                renderCarouselUI() {           const roundelWrapper = this.root.querySelector('.tg-df-carousel-roundels');           if(!roundelWrapper || !this.carouselData) return;                      let html = '';           this.carouselData.forEach(r => {              const q = r.q || r.label;              const isActive = (this.currentQuery === q || this.currentQuery === r.label) ? 'active' : '';              const imgHtml = r.img ? `\x3Cimg src="${r.img}" alt="${r.label}" />` : `\x3Csvg width="32" height="32" fill="#1F69FF" viewBox="0 0 24 24">\x3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\x3C/svg>`;              html += `                \x3Cdiv class="tg-df-roundel tg-df-carousel-cat ${isActive}" data-label="${this.escapeHTML(r.label)}">                  \x3Cdiv class="tg-df-roundel-img-box">${imgHtml}\x3C/div>                  \x3Cspan class="tg-df-roundel-label">${this.escapeHTML(r.label)}\x3C/span>                \x3C/div>              `;           });           roundelWrapper.innerHTML = html;                      // Rebind clicks           const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');           roundels.forEach(rNode => {             rNode.addEventListener('click', () => {                const r = this.carouselData.find(c => c.label === rNode.getAttribute('data-label'));                 if(!r) return;                                  if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: r.label,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = r.q || r.label;                const labelTitle = this.root.querySelector('#tg-df-carousel-title-label');                if (labelTitle) labelTitle.textContent = 'Best ' + this.currentQuery;                if (this.priceFilter) this.priceFilter.value = 'all';                if (this.customPriceMin) this.customPriceMin.value = '';                if (this.customPriceMax) this.customPriceMax.value = '';                let dPr = r.pr || 'all';                if (typeof dPr === 'string' && dPr !== 'all') {                   let prLower = dPr.toLowerCase();                   if (prLower.includes('min') || prLower.includes('over')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMin) this.customPriceMin.value = m[1];                   } else if (prLower.includes('max') || prLower.includes('under')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMax) this.customPriceMax.value = m[1];                   }                }                                let discountAmount = '0';                if(r.ds && typeof r.ds === 'string') {                   let m = r.ds.match(/(\d+)/);                   if(m) discountAmount = m[1];                }                if (this.discountFilter) this.discountFilter.value = discountAmount;                if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                                // Clear brands                    this.selectedBrands = [];                    if (this.brandDropdown) {                    const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                    chks.forEach(chk => chk.checked = false);                }                                if (this.searchInput) this.searchInput.value = this.currentQuery;                                roundels.forEach(ro => ro.classList.remove('active'));                if (rNode) rNode.classList.add('active');                                this.renderCarouselFilters(r);                this.fetchDeals(this.currentQuery);             });           });                      // Auto-highlight active           const activeR = this.carouselData.find(c => c.q === this.currentQuery || c.label === this.currentQuery);           if(activeR) this.renderCarouselFilters(activeR);        }                renderCarouselFilters(r) {           const filtersWrap = this.root.querySelector('.tg-df-carousel-filters-wrap');           if(!filtersWrap) return;                      let html = `\x3Cbutton class="tg-df-carousel-filter-btn" data-type="all">All\x3C/button>`;                      r.filters.forEach(f => {              let fL = f.toLowerCase();              let icon = '';              let logic = `data-type="custom" data-v="${this.escapeHTML(f)}"`;              if (fL === 'amazon deals' || fL === 'prime deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>`;              } else if (fL === 'lightning deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>`;              } else {                 if (fL.includes('lightning')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap">\x3Cpolygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2">\x3C/polygon>\x3C/svg>`;                 } else if (fL.includes('% off')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>`;                 } else if (fL.includes('under') || fL.includes('min ')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>`;                 }                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>${icon} ${this.escapeHTML(f)}\x3C/button>`;              }           });                      filtersWrap.innerHTML = html;                      const btns = filtersWrap.querySelectorAll('button');           btns.forEach(b => {             b.addEventListener('click', () => {                const type = b.getAttribute('data-type');                if (type === 'custom') {                   const v = b.getAttribute('data-v');                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-custom-${(v||'').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`, name: 'Custom Filter', label: v });                }                if (type === 'all') {                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: 'filter-clear-all', name: 'Clear all', label: 'Clear all filters' });                   // reset everything                   btns.forEach(btn => btn.classList.remove('active'));                   b.classList.add('active');                                      // Reset prices                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   if (this.brandDropdown) {                     const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                     chks.forEach(chk => chk.checked = false);                   }                } else {                   const v = b.getAttribute('data-v');                   const fL = v.toLowerCase();                                      let mapRet = ['amazon', 'walmart', 'best buy', 'target', 'john lewis', 'currys', 'argos'];                   const getCategory = (s) => {                      if (s === 'lightning deals' || s === 'amazon deals' || s === 'prime deals') return 'offer';                      if (s.includes('% off')) return 'discount';                      if (s.includes('under') || s.includes('over') || s.includes('min') || s.includes('max')) return 'price';                      if (mapRet.includes(s)) return 'retailer';                      return 'brand';                   };                   const cat = getCategory(fL);                   const wasActive = b.classList.contains('active');                   if (cat !== 'brand') {                      btns.forEach(btn => {                          if (btn === b) return;                          if (btn.getAttribute('data-type') === 'all') return;                          const bV = btn.getAttribute('data-v');                          if (!bV) return;                          if (getCategory(bV.toLowerCase()) === cat) btn.classList.remove('active');                      });                   }                   if (wasActive) b.classList.remove('active');                   else b.classList.add('active');                   let anyActive = Array.from(btns).some(btn => btn !== btns[0] && btn.classList.contains('active'));                   if (!anyActive) {                       btns[0].click();                       return;                   } else {                       btns[0].classList.remove('active');                   }                                      if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   btns.forEach(btn => {                       if (!btn.classList.contains('active') || btn.getAttribute('data-type') === 'all') return;                       const vv = btn.getAttribute('data-v');                       const vl = vv.toLowerCase();                                              if (vl === 'lightning deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_lightning';                       } else if (vl === 'amazon deals' || vl === 'prime deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_prime';                       } else if (vl.includes('% off')) {                          let m = vl.match(/(\d+)%/);                          if (m && this.discountFilter) this.discountFilter.value = m[1];                       } else if (vl.includes('under') || vl.includes('max')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       } else if (vl.includes('min') || vl.includes('over')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else {                          let foundR = mapRet.find(x => x === vl);                          if (foundR) {                             let realR = ['Amazon', 'Walmart', 'Best Buy', 'Target', 'John Lewis', 'Currys', 'Argos'].find(x => x.toLowerCase() === vl);                             if (this.retailerSelect) this.retailerSelect.value = realR;                          } else {                             this.selectedBrands.push(vv);                          }                       }                   });                                      if (this.brandDropdown) {                       const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                       chks.forEach(c => c.checked = this.selectedBrands.includes(c.value));                   }                                      if (r.pr && typeof r.pr === 'string') {                       let prL = r.pr.toLowerCase();                       if (prL.includes('under $')) {                           let m = prL.match(/under \$(\d+)/i);                           if (m && this.customPriceMax && !this.customPriceMax.value) this.customPriceMax.value = m[1];                       }                   }                }                                this.fetchDeals(this.currentQuery);             });           });                      // default to highlighting first           btns[0].classList.add('active');        }async fetchDeals(query, append = false) {          if (!append) {             this.showLoading();             this.deals = [];             this.displayLimit = this.getRowsLimit();          } else {             this.displayLimit += this.getRowsLimit();          }                    try {            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad(append);            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query, append);               } else {                 await this.fetchHawkDeals(query, append);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query, append);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const url = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let dynamicBrandsCounts = {};                    targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                   dynamicBrandsCounts[data.brand] = (dynamicBrandsCounts[data.brand] || 0) + 1;                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || ''                });             });          });                    const airedaleBrandsList = Object.keys(dynamicBrandsCounts).map(b => ({              formatted_value: b,              count: dynamicBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query, append = false) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (append && this.deals.length > 0) {            url.searchParams.append('offset', this.deals.length.toString());          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : (this.getViewMode() === 'savings_squad' ? 'date_desc' : 'discount_desc');          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin-top:8px;">${_div}                ${_div}              ${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)} (${this.airedaleTagCounts[tag] || 0})${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = isSavingsSquadMode ? 'View Deal' : 'Check Price';              if (isSavingsSquadMode) {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                  ${_div}                `;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = `                \x3Cdiv class="tg-df-card-merchant-wrapper">                  \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                ${_div}                \x3Cdiv class="tg-df-card-price-group">                  ${isSavingsSquadMode ? '' : `                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                  `}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description">\x3Cp>${this.escapeHTML(deal.description)}${_p}${_div}` : ''}                      ${_div}                    ${_div}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>${deal.isCheckPrice ? 'Check Price' : 'View Deal'}${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cp style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 12px; line-height: 1.4;">${this.escapeHTML(deal.description)}${_p}` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${priceGroupHtml}                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta ${isSavingsSquadMode ? 'tg-df-cta-savings-squad' : ''}" style="text-decoration: none;">${ctaText}${_a}                  ${_div}                ${_div}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit || ((this.getViewMode() === 'carousel' || this.getViewMode() === 'auto') && displayDeals.length > 0 && displayDeals.length % ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12) === 0)) {            if (this.getViewMode() === 'carousel') {               dealsHtml += `                 \x3Cbutton type="button" class="tg-df-load-more-card tg-df-load-more">                   \x3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;">\x3Cpath d="M5 12h14">\x3C/path>\x3Cpath d="m12 5 7 7-7 7">\x3C/path>\x3C/svg>                   Load More                 ${_button}               `;            } else {               dealsHtml += `                 \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                   \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer; display: flex; align-items: center;">Load More${_button}                 ${_div}               `;            }          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let rightChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             let leftChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-left');             if (this.getViewMode() === 'carousel') {                 // The observer set up in setupScrollListeners handles visibility.                 if (rightChevron) rightChevron.style.display = 'flex';                 if (leftChevron) leftChevron.style.display = 'none'; // reset correctly             } else {                 if (rightChevron) rightChevron.style.display = 'none';                 if (leftChevron) leftChevron.style.display = 'none';             }          }                    const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', async () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              if (displayDeals.length <= this.displayLimit) {                 loadMoreBtn.innerHTML = `                  <svg class="tg-df-spinner" style="width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 8px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/></svg>                  Loading...                 `;                 loadMoreBtn.disabled = true;                 await this.fetchDeals(this.currentQuery, true);              } else {                 this.displayLimit += this.getRowsLimit();                 this.render();              }            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            setTimeout(() => {               if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');               document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));            }, 50);          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tried this oven cleaning hack using two household items I already own — and I was surprised by the results ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Cleaning my oven is one of those jobs I <em>always</em> put off. It just seem so laborious that I end up delaying cleaning it at all, which only makes it so much worse. I've found myself staring at oven cleaning products, unsure which one to choose and worrying about how toxic they are or how much elbow grease I'll need to put in to actually achieve results I'm pleased with. Unsurprisingly with all this overthinking, I end up giving up before I've even started.</p><p>But, then I saw a TikTok hack that suggested I would only need to use two items which I already happen to own (and it's likely you do too). With just a bottle of Cif and some cling wrap to hand, it seemed like a very affordable way to get my oven clean and the video I saw also made it look so simple. But... is it really <em>that</em> easy? Here's what happened when I tried it.</p><h2 id="how-does-it-work">How does it work?</h2>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@cleanipedia_uk/video/7449465187083488544" data-video-id="7449465187083488544" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@cleanipedia_uk" href="https://www.tiktok.com/@cleanipedia_uk">@cleanipedia_uk</a>                            <p></p><a target="_blank" title="♬ Pixel Love - Todd Helder" href="https://www.tiktok.com/music/Pixel-Love-7103728508862253058">♬ Pixel Love - Todd Helder</a></section>                    </blockquote></div>                <p>TikTok page, <a href="https://www.tiktok.com/@cleanipedia_uk" target="_blank" rel="nofollow">@cleanipedia_uk</a>, posts expert cleaning hacks and tips by Unilever to its almost 70k followerss. But, it's this specific oven cleaning hack (featured above) that really caught my eye. </p><p>Using just Cif and some cling wrap, both of which are budget-friendly, it demonstrates that your oven can go from caked in built-up grease to completely clean (apparently). And all it takes is just five simple steps.</p><p><strong>To follow this hack, you can:</strong></p><ul><li><strong>Apply Cif Cream to your oven door</strong></li><li><strong>Cover with cling wrap and spread the cream underneath</strong></li><li><strong>Wait for around an hour</strong></li><li><strong>Remove the cling wrap and scrub with a non-abrasive sponge</strong></li><li><strong>Wipe away everything with a damp microfibre cloth</strong></li></ul><p>While users in the comments were worried that the Cif would leave a smell, Cleanipedia replied: "Just make sure you wipe and rinse properly!" And some users suggested you should just be cleaning your oven whenever you use it. </p><p>For me, as much as I want to be a person that remembers to clean their oven every time it gets used, I'll never remember to go back after it's cooled down. Inevitably, my oven just keeps getting dirtier. So, a hack like this seems like a good alternative.</p><div class="product"><a data-dimension112="41d6abb4-7aab-11f1-846c-8504f51daa0b" data-action="Deal Block" data-label="Lemon Cream Cleaner" data-dimension48="Lemon Cream Cleaner" data-dimension25="$9.19" href="https://www.amazon.com/Cif-Cream-Lemon-500-ml/dp/B01N4IHSX3/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="eoo74jszxU4ZCr6sLQYGPX" name="cif_cream_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/eoo74jszxU4ZCr6sLQYGPX.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Cif-Cream-Lemon-500-ml/dp/B01N4IHSX3/" target="_blank" rel="nofollow" data-dimension112="41d6abb4-7aab-11f1-846c-8504f51daa0b" data-action="Deal Block" data-label="Lemon Cream Cleaner" data-dimension48="Lemon Cream Cleaner" data-dimension25="$9.19">Lemon Cream Cleaner: $9.19</a></strong><br>I swear by Cif cream for cleaning my kitchen sink, so I've always got a bottle around. But, if you don't, it's pretty inexpensive to pick up and I found that it's definitely more affordable and has more uses around the home than buying oven cleaning specific products. <a class="view-deal button" href="https://www.amazon.com/Cif-Cream-Lemon-500-ml/dp/B01N4IHSX3/" target="_blank" rel="nofollow" data-dimension112="41d6abb4-7aab-11f1-846c-8504f51daa0b" data-action="Deal Block" data-label="Lemon Cream Cleaner" data-dimension48="Lemon Cream Cleaner" data-dimension25="$9.19">View Deal</a></p></div><h2 id="here-s-what-happened-when-i-tried-it">Here's what happened when I tried it</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="MLne4eAc6aNzEkPK6vBSYF" name="oven_cleaning_hack_1" alt="An oven door before cleaning" src="https://cdn.mos.cms.futurecdn.net/MLne4eAc6aNzEkPK6vBSYF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Armed with my bottle of Cif, some cling wrap and a dream, I thought I'd give it a go. Now, I'm aware that my oven is a bit of a mess, hence needing a TikTok hack like this to hopefully save me. And following five steps seemed simple enough – and it was. </p><p>I applied my Cif Cream and covered it in cling wrap, which took about 1 minutes (2 when wrestling with the cling wrap tube) and I decided to leave it for an hour, as recommended. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rMa7VsFzDkokEGe24grJhF" name="oven_cleaning_hack_3" alt="Cling wrap and Cif on an oven door" src="https://cdn.mos.cms.futurecdn.net/rMa7VsFzDkokEGe24grJhF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>After an hour, I went back to my oven and I was a little concerned that it didn't look like the Cif was infiltrating the grease the way the video had shown. As Cleanipedia suggests though, the longer you leave it the better, so I left it for another hour before returning to give it a scrub.</p><p>I'll start by saying I was surprised about how much elbow grease was still needed to clean off the grease, but I was ultimately still impressed that it took away a lot of the grease from the main part of the door.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ipVmsv7wKj3a227YAYhKhF" name="oven_cleaning_hack_4" alt="An oven door after cleaning" src="https://cdn.mos.cms.futurecdn.net/ipVmsv7wKj3a227YAYhKhF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>As you can see from the picture above though, it didn't tackle the caked-on grease nearly as well as I'd hoped. But, I'm not done trying this method. I'll leave it on for much longer next time and I'll make sure my sponge is a little more ready to scour. </p><p>It's definitely something I'll be trying again and I loved how easy it was. I also felt more comfortable leaving the Cif on the oven door for a length of time when I knew it was covered up. It was easy to forget about and if I'd forgotten about it for longer, maybe I would've gotten better results.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/skip-the-baking-soda-and-vinegar-i-used-this-non-toxic-substance-to-clean-through-my-ovens-grime-and-im-speechless">Skip the baking soda and vinegar — I used this non-toxic substance to clean through my oven's grime, and I'm speechless</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/this-vinegar-steam-trick-will-rid-your-oven-of-grease-and-grime-heres-how-to-do-it">This vinegar steam trick will rid your oven of grease and grime — here's how to do it</a></li><li><a href="https://www.tomsguide.com/home/how-to-remove-oven-glass-stains-without-leaving-a-single-scratch-using-this-one-natural-solution">How to remove oven glass stains without leaving a single scratch using this one natural solution</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/i-tried-this-oven-cleaning-hack-using-two-household-items-i-already-own-and-i-was-surprised-by-the-results</link>
                                                                            <description>
                            <![CDATA[ Cleaning my oven is a household job I love to procrastinate. But, then it becomes a shameful part of my kitchen. Here's the simple TikTok hack that I tried out to try and turn oven cleaning into a job I'm happy to do. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">AKRAGimwaBbJkudGV9QpiD</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/UC5UBbmdmqNxKrEeBVgBhF-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 11 Jul 2026 07:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                                    <dc:creator><![CDATA[ Grace Dean ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oxXqkks7wgxZkPiyYY2n6H.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ null ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/UC5UBbmdmqNxKrEeBVgBhF-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Rubber gloves, Cif and cling wrap on a surface]]></media:description>                                                            <media:text><![CDATA[Rubber gloves, Cif and cling wrap on a surface]]></media:text>
                                <media:title type="plain"><![CDATA[Rubber gloves, Cif and cling wrap on a surface]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/UC5UBbmdmqNxKrEeBVgBhF-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Cleaning my oven is one of those jobs I <em>always</em> put off. It just seem so laborious that I end up delaying cleaning it at all, which only makes it so much worse. I've found myself staring at oven cleaning products, unsure which one to choose and worrying about how toxic they are or how much elbow grease I'll need to put in to actually achieve results I'm pleased with. Unsurprisingly with all this overthinking, I end up giving up before I've even started.</p><p>But, then I saw a TikTok hack that suggested I would only need to use two items which I already happen to own (and it's likely you do too). With just a bottle of Cif and some cling wrap to hand, it seemed like a very affordable way to get my oven clean and the video I saw also made it look so simple. But... is it really <em>that</em> easy? Here's what happened when I tried it.</p><h2 id="how-does-it-work">How does it work?</h2>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@cleanipedia_uk/video/7449465187083488544" data-video-id="7449465187083488544" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@cleanipedia_uk" href="https://www.tiktok.com/@cleanipedia_uk">@cleanipedia_uk</a>                            <p></p><a target="_blank" title="♬ Pixel Love - Todd Helder" href="https://www.tiktok.com/music/Pixel-Love-7103728508862253058">♬ Pixel Love - Todd Helder</a></section>                    </blockquote></div>                <p>TikTok page, <a href="https://www.tiktok.com/@cleanipedia_uk" target="_blank" rel="nofollow">@cleanipedia_uk</a>, posts expert cleaning hacks and tips by Unilever to its almost 70k followerss. But, it's this specific oven cleaning hack (featured above) that really caught my eye. </p><p>Using just Cif and some cling wrap, both of which are budget-friendly, it demonstrates that your oven can go from caked in built-up grease to completely clean (apparently). And all it takes is just five simple steps.</p><p><strong>To follow this hack, you can:</strong></p><ul><li><strong>Apply Cif Cream to your oven door</strong></li><li><strong>Cover with cling wrap and spread the cream underneath</strong></li><li><strong>Wait for around an hour</strong></li><li><strong>Remove the cling wrap and scrub with a non-abrasive sponge</strong></li><li><strong>Wipe away everything with a damp microfibre cloth</strong></li></ul><p>While users in the comments were worried that the Cif would leave a smell, Cleanipedia replied: "Just make sure you wipe and rinse properly!" And some users suggested you should just be cleaning your oven whenever you use it. </p><p>For me, as much as I want to be a person that remembers to clean their oven every time it gets used, I'll never remember to go back after it's cooled down. Inevitably, my oven just keeps getting dirtier. So, a hack like this seems like a good alternative.</p><div class="product"><a data-dimension112="41d6abb4-7aab-11f1-846c-8504f51daa0b" data-action="Deal Block" data-label="Lemon Cream Cleaner" data-dimension48="Lemon Cream Cleaner" data-dimension25="$9.19" href="https://www.amazon.com/Cif-Cream-Lemon-500-ml/dp/B01N4IHSX3/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:535px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="eoo74jszxU4ZCr6sLQYGPX" name="cif_cream_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/eoo74jszxU4ZCr6sLQYGPX.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Cif-Cream-Lemon-500-ml/dp/B01N4IHSX3/" target="_blank" rel="nofollow" data-dimension112="41d6abb4-7aab-11f1-846c-8504f51daa0b" data-action="Deal Block" data-label="Lemon Cream Cleaner" data-dimension48="Lemon Cream Cleaner" data-dimension25="$9.19">Lemon Cream Cleaner: $9.19</a></strong><br>I swear by Cif cream for cleaning my kitchen sink, so I've always got a bottle around. But, if you don't, it's pretty inexpensive to pick up and I found that it's definitely more affordable and has more uses around the home than buying oven cleaning specific products. <a class="view-deal button" href="https://www.amazon.com/Cif-Cream-Lemon-500-ml/dp/B01N4IHSX3/" target="_blank" rel="nofollow" data-dimension112="41d6abb4-7aab-11f1-846c-8504f51daa0b" data-action="Deal Block" data-label="Lemon Cream Cleaner" data-dimension48="Lemon Cream Cleaner" data-dimension25="$9.19">View Deal</a></p></div><h2 id="here-s-what-happened-when-i-tried-it">Here's what happened when I tried it</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="MLne4eAc6aNzEkPK6vBSYF" name="oven_cleaning_hack_1" alt="An oven door before cleaning" src="https://cdn.mos.cms.futurecdn.net/MLne4eAc6aNzEkPK6vBSYF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Armed with my bottle of Cif, some cling wrap and a dream, I thought I'd give it a go. Now, I'm aware that my oven is a bit of a mess, hence needing a TikTok hack like this to hopefully save me. And following five steps seemed simple enough – and it was. </p><p>I applied my Cif Cream and covered it in cling wrap, which took about 1 minutes (2 when wrestling with the cling wrap tube) and I decided to leave it for an hour, as recommended. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rMa7VsFzDkokEGe24grJhF" name="oven_cleaning_hack_3" alt="Cling wrap and Cif on an oven door" src="https://cdn.mos.cms.futurecdn.net/rMa7VsFzDkokEGe24grJhF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>After an hour, I went back to my oven and I was a little concerned that it didn't look like the Cif was infiltrating the grease the way the video had shown. As Cleanipedia suggests though, the longer you leave it the better, so I left it for another hour before returning to give it a scrub.</p><p>I'll start by saying I was surprised about how much elbow grease was still needed to clean off the grease, but I was ultimately still impressed that it took away a lot of the grease from the main part of the door.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ipVmsv7wKj3a227YAYhKhF" name="oven_cleaning_hack_4" alt="An oven door after cleaning" src="https://cdn.mos.cms.futurecdn.net/ipVmsv7wKj3a227YAYhKhF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>As you can see from the picture above though, it didn't tackle the caked-on grease nearly as well as I'd hoped. But, I'm not done trying this method. I'll leave it on for much longer next time and I'll make sure my sponge is a little more ready to scour. </p><p>It's definitely something I'll be trying again and I loved how easy it was. I also felt more comfortable leaving the Cif on the oven door for a length of time when I knew it was covered up. It was easy to forget about and if I'd forgotten about it for longer, maybe I would've gotten better results.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/skip-the-baking-soda-and-vinegar-i-used-this-non-toxic-substance-to-clean-through-my-ovens-grime-and-im-speechless">Skip the baking soda and vinegar — I used this non-toxic substance to clean through my oven's grime, and I'm speechless</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/this-vinegar-steam-trick-will-rid-your-oven-of-grease-and-grime-heres-how-to-do-it">This vinegar steam trick will rid your oven of grease and grime — here's how to do it</a></li><li><a href="https://www.tomsguide.com/home/how-to-remove-oven-glass-stains-without-leaving-a-single-scratch-using-this-one-natural-solution">How to remove oven glass stains without leaving a single scratch using this one natural solution</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested Nespresso’s brand-new Vertuo Up — and it has a dedicated ‘Over Ice’ mode for summer ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Nespresso Vertuo Up is the newest Nespresso machine as of July 2026. It brings back the iconic Nespresso plug-and-play brew style with an added “Over Ice” mode and app functionality. Yes, this coffee machine connects to my phone, and I’m actually not mad about it. </p><p>But is this enough to make it one of the <a href="https://www.tomsguide.com/best-picks/best-nespresso-machine">best Nespresso machines</a> you can get? Heck, yeah! I loved testing this machine. It’s as easy as pie — or, as easy as Nespresso — and doesn’t look particularly hideous while doing it. Of course, it’s not as premium as the Nespresso Vertuo Creatista, but it’s also $400 cheaper. </p><p>I had a great time testing this machine, and so did my colleagues. One of my colleagues drank <em>four</em> cups of coffee in one day during my testing period — that’s how good it is. Want to find out more about the new Nespresso machine? Why wouldn’t you? Keep reading this Nespresso Vertuo Up review. </p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-specs"><span>Nespresso Vertuo Up review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Nespresso-Vertuo-Espresso-DeLonghi-Graphite/dp/B0GLFXWT6Z" target="_blank" rel="nofollow">$229</a> / <a href="https://www.amazon.co.uk/Nespresso-Precision-Adjustable-Americano-XN9408G0/dp/B0GKNYJ92F" target="_blank" rel="nofollow">£179</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>9 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>15.4 x 8.7 x 10.1 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Grinder</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Heating system </strong></p></td><td  ><p>3 seconds</p></td></tr><tr><td class="firstcol " ><p><strong>Pressure </strong></p></td><td  ><p>Not specified - Vertuo pods use centrifugal force</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank capacity</strong></p></td><td  ><p>47 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>Drip tray height adjustment</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-price-availability"><span>Nespresso Vertuo Up review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vUtZHYJjZqnPgG5hMDU5tX" name="Nespresso_Vertuo_Up 11.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/vUtZHYJjZqnPgG5hMDU5tX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Nespresso Vertuo Up is <a href="https://www.amazon.com/Nespresso-Vertuo-Espresso-DeLonghi-Graphite/dp/B0GLFXWT6Z" target="_blank" rel="nofollow">$229 from Amazon U.S.</a> and <a href="https://www.amazon.co.uk/Nespresso-Precision-Adjustable-Americano-XN9408G0/dp/B0GKNYJ92F" target="_blank" rel="nofollow">£179 from Amazon U.K.</a>. Nespresso makes two lines of machines: original and Vertuo. Of the Vertuo collection, the Up is the most premium. If you want similar performance for less, you’d be looking at the $189 <a href="https://www.tomsguide.com/reviews/nespresso-vertuo-next">Vertuo Next </a>or the $129 <a href="https://www.tomsguide.com/home/coffee-makers/i-spent-a-week-testing-nespressos-cutest-coffee-maker-my-verdict-on-the-vertuo-pop-plus">Vertuo Pop+</a>. </p><p>Vertuo machines are generally cheaper than original Nespresso machines. This is because Vertuo pods are patented, whereas original Nespresso pods are not. Ergo, Nespresso charges less for the initial machine, but more for the pods… so you might end up paying more over time. </p><p>The <a href="https://www.tomsguide.com/home/kitchen-dining/nespresso-citiz-coffee-maker-review">Nespresso CitiZ</a> ($265) is my favorite original Nespresso machine, but you can still get the three-year-old <a href="https://www.tomsguide.com/reviews/nespresso-pixie">Nespresso Pixie</a> ($229). Of course, neither of these machines have app connectivity, so you can’t adjust the heat or size of your drinks. </p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-design"><span>Nespresso Vertuo Up review: Design </span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="55P6dzyFkdL2Vvxzk9LNZX" name="Nespresso_Vertuo_Up 6.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/55P6dzyFkdL2Vvxzk9LNZX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Nespresso Vertuo Up, like other Nespresso machines, is compact and ideal for small spaces. As it’s just 4.7 inches wide, I was able to fit it in my kitchen with zero stress. </p><p>However, it’s worth noting that it needs a decent amount of overhead clearance — the pod release comes up 5 inches, giving it an overall height of 15.9 inches. Because of this, I wouldn’t recommend putting it under low-lying cabinets. </p><p>One of my favorite design features is the rotating water tank. Unlike other Nespresso machines (CitiZ), the water tank can fit on the back or on either side, so you can adapt the machine to fit your space rather than the other way around. </p><p>Of course, the Vertuo Up is relatively plasticky. It <em>is</em> a Nespresso machine, after all. If you want a more premium aesthetic, I’d recommend checking out the eye-wateringly expensive <a href="https://www.tomsguide.com/home/coffee-makers/nespresso-vertuo-creatista-review">Vertuo Creatista</a> ($699). </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="icWuY8DpsU7KfarGdHSVsX" name="Nespresso_Vertuo_Up 7.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/icWuY8DpsU7KfarGdHSVsX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Another “it <em>is</em> a Nespresso machine, after all” design feature is the minimal on-machine controls. Instead of levers, buttons, and dials you might find on espresso machines like the De’Longhi Stilosa ($149) or Breville Bambino ($299), the Vertuo Up has just two buttons: “Ice” and “Go”. Those aren’t the official names, of course, but they explain what I mean well enough.</p><p>If you’re using “Over Ice” pods (like I was), you’ll need to press the “Ice” button before “Go”. Otherwise, you’ll get a hot drink. Obviously, this is great if you want to stick it to the man and brew a hot coffee using “Over Ice” pods, but, as I experienced, pods designed for iced drinks don’t taste great hot. </p><p>Unlike Nespresso original machines, you can’t choose between an espresso or lungo shot. Each Vertuo pod has a specific dosage volume: for example, the Coconut & Vanilla is a 230ml “mug”, whereas the Yuzu & Vanilla is an 80ml “double shot”. You can <em>kind of</em> adjust volumes with the NespressoSmart app (more on that later), but you can’t do it on the machine itself. This is a little frustrating, and I’d like to see Nespresso utilize on-machine buttons to correct this in the future, as not everyone wants or needs the Nespresso app on their phone. </p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-performance"><span>Nespresso Vertuo Up review: Performance</span></h2><p>The Nespresso Vertuo Up is supremely easy to use. I’ll say it for the third time: it <em>is</em> a Nespresso machine, after all. These things are revered for their ease of use and simplicity. </p><p>All you have to do is insert the pod of your choice and press “Go”. That’s it. The pod starts spinning like an errant hair caught in a plughole, and thirty seconds later, you’ve got a fresh coffee. </p><p>I will say that the Vertuo Up is incredibly loud: it peaked at 70.9dB. It was <em>so</em> loud, in fact, I had colleagues come up to me and say things like, “Oh, that’s a bit of a screamer, isn’t it?” Yes. Yes it is. </p><p>Even so, this isn’t a dealbreaker for me. I’m using the machine for, what, a minute? I can put up with 71dB for a minute in exchange for fresh coffee. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Yt7hsorFdn52WjbPv7kFf5.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/VCty5TSsiKbSPQwgvCRspY.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/7H3a3gL644uxDxY6mYepCP.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Cjh4KwZZkksQHwuVjFk3yM.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure></figure><p>Taste is tricky to evaluate because different pods taste different, and different people have different palates. I tested the Vertuo Up with the Yuzu & Vanilla, Coconut & Vanilla, and Oatly Barista Edition Over Ice pods. They all tasted great, with zero burned or bitter notes (except once).</p><p>Let me elaborate. I’ll preface this by saying this is purely user error. This is simply advice to you — not an issue with the Vertuo Up at all. I mistakenly brewed the Yuzu & Vanilla pod hot rather than “Over Ice”; it tasted super bitter, with a really frothy crema. It was, put simply, not good. So learn from my mistakes and follow the instructions on the pod packaging! </p><p>The only <em>real</em> con I have with the Nespresso Vertuo Up is its tendency to leak before extraction. After inserting a pod and pressing “Go”, the machine leaks around half a teaspoon of coffee into the cup, presumably leftover coffee stuck in the dispensing nozzle from the previous brew. Kinda gross. Although I’m sure this didn’t majorly impact the flavor of my subsequent coffee, it’s worth noting. I’d like to see Nespresso rectify this for the next model, whenever that may be. There’s no “clean” option between drinks.</p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-app"><span>Nespresso Vertuo Up review: App</span></h2><p>I’m usually an app hater, but I really like the NespressoSmart app. It actually benefits the user experience.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1600px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nT9ZSpsJdNZnzTUviPgxCN" name="nespresso vertuo up screenshots" alt="three screenshots from the nespresso smart app showing functionality with the nespresso vertuo up" src="https://cdn.mos.cms.futurecdn.net/nT9ZSpsJdNZnzTUviPgxCN.png" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide / Nespresso)</span></figcaption></figure><p>As you can see, the app is clean, easy to use, and user-friendly. I can see even the most technophobic person on the planet being able to make the most of this app. You can change the volume of your pods, change water temperature, run cleaning cycles, run water emptying cycles, and look up instruction manuals. </p><p>Of course, I would like to see some on-machine controls for these settings as not everyone wants or needs to download an app to use a coffee machine, but as far as apps go, this is a decent one. </p><p>The only downside is that the app asks you for full contact information while signing up. You have to put in your full name, email, full address, and phone number. Why? Why does the app need this info for me to customize my espresso? I just wrote “No” in all the address boxes, put a random zip code, and a fake number and it let me make the account, but this is still annoying. </p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-storage-maintenance"><span>Nespresso Vertuo Up review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="a8urLUZzPQsRu4XVvKxiuX" name="Nespresso_Vertuo_Up 1.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/a8urLUZzPQsRu4XVvKxiuX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As this is a Vertuo machine, replacement pods are only available from Nespresso’s official Amazon store and Nespresso direct. This is disappointing. I wanted to buy some extra pods and visited my local grocery store (a large store with tons of products, including Nespresso pods), but it only stocked original pods. After a quick Google, I discovered that I could only buy these specific pods online. </p><p>Therefore, you’ll need some pre-planning to ensure you don’t run out of pods. The price of pods is also quite different from original Nespresso pods. A 30-pack of OG Nespresso pods retails for $27, working out to $0.90 per pod. Vertuo pods are $40.50 for the same sized pack, $1.35 per pod. The pods I personally tested with were around $1.25-$1.55 per pod. </p><p>So while the Vertuo machines are cheaper from the off, you might actually spend more money over time. Let’s quickly do some coffee math. Say you’re spending $13 extra a month (if you drink one a day). There’s a $36 price difference between the Vertuo Up and the Citiz. After 3 months, you’ll have spent that extra $36 on Nespresso’s patented Vertuo pods. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gb23XMYiL2ueRX7hT2ApuX" name="Nespresso_Vertuo_Up 8.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/gb23XMYiL2ueRX7hT2ApuX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Personally, I would always buy an original Nespresso machine for this reason, but of course, the decision is up to you. </p><p>Cleaning the machine is straightforward: there are detailed instructions in the app and you can start cleaning cycles from your phone. You can also buy cleaning kits directly from the app: a descaling kit is $10.99 for two sachets. </p><p>Alright, last but not least: pod recycling. Of course, not buying single-use pods is inherently more environmentally friendly than buying single-use pods, but at least you can recycle Nespresso pods. You can either get a recycling bag direct from Nespresso, return your pods to Nespresso stores, or drop off your pods at select convenience stores. </p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-verdict"><span>Nespresso Vertuo Up review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rWaXwVXAGNU9MbN3Uxj5wX" name="Nespresso_Vertuo_Up 10.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/rWaXwVXAGNU9MbN3Uxj5wX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Nespresso Vertuo Up is a really great Nespresso machine. It’s beyond easy to use, looks pretty good, and the app is genuinely useful. While it’s not perfect, hence the 4-star rating, it’s bound to make any hands-free coffee lover happy. My colleagues all remarked on its ease of use and pleasant coffee flavor. </p><p>Sure, I’d appreciate some on-machine controls for the next gen and I’d appreciate more pod options, but if you want to save that initial cost with a Vertuo, this is a pretty good one to get. It can make iced and hot coffees in seconds, and never made a bad brew (except my experiment). </p><p>I recommend the Nespresso Vertuo Up for anyone interested in upgrading an old Nespresso machine, anyone who particularly likes having smart control features on their coffee machine, or, simply, anyone who wants hot (or cold) coffee and wants it now. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/nespresso-vertuo-up-review</link>
                                                                            <description>
                            <![CDATA[ Nespresso Vertuo Up is a futureproofed machine with app functionality, a new iced coffee mode, and a rotating water tank. Of course, it’s easy and fun to use. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">4ZSYoAcjGhZrJPLs3FX2kY</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/xoXeUiySauLYqBVNtx7ctX-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 10 Jul 2026 12:03:04 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/xoXeUiySauLYqBVNtx7ctX-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the nespresso vertuo up machine photographed against a blue background]]></media:description>                                                            <media:text><![CDATA[the nespresso vertuo up machine photographed against a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[the nespresso vertuo up machine photographed against a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/xoXeUiySauLYqBVNtx7ctX-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Nespresso Vertuo Up is the newest Nespresso machine as of July 2026. It brings back the iconic Nespresso plug-and-play brew style with an added “Over Ice” mode and app functionality. Yes, this coffee machine connects to my phone, and I’m actually not mad about it. </p><p>But is this enough to make it one of the <a href="https://www.tomsguide.com/best-picks/best-nespresso-machine">best Nespresso machines</a> you can get? Heck, yeah! I loved testing this machine. It’s as easy as pie — or, as easy as Nespresso — and doesn’t look particularly hideous while doing it. Of course, it’s not as premium as the Nespresso Vertuo Creatista, but it’s also $400 cheaper. </p><p>I had a great time testing this machine, and so did my colleagues. One of my colleagues drank <em>four</em> cups of coffee in one day during my testing period — that’s how good it is. Want to find out more about the new Nespresso machine? Why wouldn’t you? Keep reading this Nespresso Vertuo Up review. </p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-specs"><span>Nespresso Vertuo Up review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Nespresso-Vertuo-Espresso-DeLonghi-Graphite/dp/B0GLFXWT6Z" target="_blank" rel="nofollow">$229</a> / <a href="https://www.amazon.co.uk/Nespresso-Precision-Adjustable-Americano-XN9408G0/dp/B0GKNYJ92F" target="_blank" rel="nofollow">£179</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>9 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>15.4 x 8.7 x 10.1 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Grinder</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Heating system </strong></p></td><td  ><p>3 seconds</p></td></tr><tr><td class="firstcol " ><p><strong>Pressure </strong></p></td><td  ><p>Not specified - Vertuo pods use centrifugal force</p></td></tr><tr><td class="firstcol " ><p><strong>Water tank capacity</strong></p></td><td  ><p>47 fluid ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Accessories</strong></p></td><td  ><p>Drip tray height adjustment</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-price-availability"><span>Nespresso Vertuo Up review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vUtZHYJjZqnPgG5hMDU5tX" name="Nespresso_Vertuo_Up 11.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/vUtZHYJjZqnPgG5hMDU5tX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Nespresso Vertuo Up is <a href="https://www.amazon.com/Nespresso-Vertuo-Espresso-DeLonghi-Graphite/dp/B0GLFXWT6Z" target="_blank" rel="nofollow">$229 from Amazon U.S.</a> and <a href="https://www.amazon.co.uk/Nespresso-Precision-Adjustable-Americano-XN9408G0/dp/B0GKNYJ92F" target="_blank" rel="nofollow">£179 from Amazon U.K.</a>. Nespresso makes two lines of machines: original and Vertuo. Of the Vertuo collection, the Up is the most premium. If you want similar performance for less, you’d be looking at the $189 <a href="https://www.tomsguide.com/reviews/nespresso-vertuo-next">Vertuo Next </a>or the $129 <a href="https://www.tomsguide.com/home/coffee-makers/i-spent-a-week-testing-nespressos-cutest-coffee-maker-my-verdict-on-the-vertuo-pop-plus">Vertuo Pop+</a>. </p><p>Vertuo machines are generally cheaper than original Nespresso machines. This is because Vertuo pods are patented, whereas original Nespresso pods are not. Ergo, Nespresso charges less for the initial machine, but more for the pods… so you might end up paying more over time. </p><p>The <a href="https://www.tomsguide.com/home/kitchen-dining/nespresso-citiz-coffee-maker-review">Nespresso CitiZ</a> ($265) is my favorite original Nespresso machine, but you can still get the three-year-old <a href="https://www.tomsguide.com/reviews/nespresso-pixie">Nespresso Pixie</a> ($229). Of course, neither of these machines have app connectivity, so you can’t adjust the heat or size of your drinks. </p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-design"><span>Nespresso Vertuo Up review: Design </span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="55P6dzyFkdL2Vvxzk9LNZX" name="Nespresso_Vertuo_Up 6.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/55P6dzyFkdL2Vvxzk9LNZX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Nespresso Vertuo Up, like other Nespresso machines, is compact and ideal for small spaces. As it’s just 4.7 inches wide, I was able to fit it in my kitchen with zero stress. </p><p>However, it’s worth noting that it needs a decent amount of overhead clearance — the pod release comes up 5 inches, giving it an overall height of 15.9 inches. Because of this, I wouldn’t recommend putting it under low-lying cabinets. </p><p>One of my favorite design features is the rotating water tank. Unlike other Nespresso machines (CitiZ), the water tank can fit on the back or on either side, so you can adapt the machine to fit your space rather than the other way around. </p><p>Of course, the Vertuo Up is relatively plasticky. It <em>is</em> a Nespresso machine, after all. If you want a more premium aesthetic, I’d recommend checking out the eye-wateringly expensive <a href="https://www.tomsguide.com/home/coffee-makers/nespresso-vertuo-creatista-review">Vertuo Creatista</a> ($699). </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="icWuY8DpsU7KfarGdHSVsX" name="Nespresso_Vertuo_Up 7.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/icWuY8DpsU7KfarGdHSVsX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Another “it <em>is</em> a Nespresso machine, after all” design feature is the minimal on-machine controls. Instead of levers, buttons, and dials you might find on espresso machines like the De’Longhi Stilosa ($149) or Breville Bambino ($299), the Vertuo Up has just two buttons: “Ice” and “Go”. Those aren’t the official names, of course, but they explain what I mean well enough.</p><p>If you’re using “Over Ice” pods (like I was), you’ll need to press the “Ice” button before “Go”. Otherwise, you’ll get a hot drink. Obviously, this is great if you want to stick it to the man and brew a hot coffee using “Over Ice” pods, but, as I experienced, pods designed for iced drinks don’t taste great hot. </p><p>Unlike Nespresso original machines, you can’t choose between an espresso or lungo shot. Each Vertuo pod has a specific dosage volume: for example, the Coconut & Vanilla is a 230ml “mug”, whereas the Yuzu & Vanilla is an 80ml “double shot”. You can <em>kind of</em> adjust volumes with the NespressoSmart app (more on that later), but you can’t do it on the machine itself. This is a little frustrating, and I’d like to see Nespresso utilize on-machine buttons to correct this in the future, as not everyone wants or needs the Nespresso app on their phone. </p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-performance"><span>Nespresso Vertuo Up review: Performance</span></h2><p>The Nespresso Vertuo Up is supremely easy to use. I’ll say it for the third time: it <em>is</em> a Nespresso machine, after all. These things are revered for their ease of use and simplicity. </p><p>All you have to do is insert the pod of your choice and press “Go”. That’s it. The pod starts spinning like an errant hair caught in a plughole, and thirty seconds later, you’ve got a fresh coffee. </p><p>I will say that the Vertuo Up is incredibly loud: it peaked at 70.9dB. It was <em>so</em> loud, in fact, I had colleagues come up to me and say things like, “Oh, that’s a bit of a screamer, isn’t it?” Yes. Yes it is. </p><p>Even so, this isn’t a dealbreaker for me. I’m using the machine for, what, a minute? I can put up with 71dB for a minute in exchange for fresh coffee. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Yt7hsorFdn52WjbPv7kFf5.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/VCty5TSsiKbSPQwgvCRspY.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/7H3a3gL644uxDxY6mYepCP.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Cjh4KwZZkksQHwuVjFk3yM.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure></figure><p>Taste is tricky to evaluate because different pods taste different, and different people have different palates. I tested the Vertuo Up with the Yuzu & Vanilla, Coconut & Vanilla, and Oatly Barista Edition Over Ice pods. They all tasted great, with zero burned or bitter notes (except once).</p><p>Let me elaborate. I’ll preface this by saying this is purely user error. This is simply advice to you — not an issue with the Vertuo Up at all. I mistakenly brewed the Yuzu & Vanilla pod hot rather than “Over Ice”; it tasted super bitter, with a really frothy crema. It was, put simply, not good. So learn from my mistakes and follow the instructions on the pod packaging! </p><p>The only <em>real</em> con I have with the Nespresso Vertuo Up is its tendency to leak before extraction. After inserting a pod and pressing “Go”, the machine leaks around half a teaspoon of coffee into the cup, presumably leftover coffee stuck in the dispensing nozzle from the previous brew. Kinda gross. Although I’m sure this didn’t majorly impact the flavor of my subsequent coffee, it’s worth noting. I’d like to see Nespresso rectify this for the next model, whenever that may be. There’s no “clean” option between drinks.</p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-app"><span>Nespresso Vertuo Up review: App</span></h2><p>I’m usually an app hater, but I really like the NespressoSmart app. It actually benefits the user experience.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1600px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nT9ZSpsJdNZnzTUviPgxCN" name="nespresso vertuo up screenshots" alt="three screenshots from the nespresso smart app showing functionality with the nespresso vertuo up" src="https://cdn.mos.cms.futurecdn.net/nT9ZSpsJdNZnzTUviPgxCN.png" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide / Nespresso)</span></figcaption></figure><p>As you can see, the app is clean, easy to use, and user-friendly. I can see even the most technophobic person on the planet being able to make the most of this app. You can change the volume of your pods, change water temperature, run cleaning cycles, run water emptying cycles, and look up instruction manuals. </p><p>Of course, I would like to see some on-machine controls for these settings as not everyone wants or needs to download an app to use a coffee machine, but as far as apps go, this is a decent one. </p><p>The only downside is that the app asks you for full contact information while signing up. You have to put in your full name, email, full address, and phone number. Why? Why does the app need this info for me to customize my espresso? I just wrote “No” in all the address boxes, put a random zip code, and a fake number and it let me make the account, but this is still annoying. </p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-storage-maintenance"><span>Nespresso Vertuo Up review: Storage & maintenance</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="a8urLUZzPQsRu4XVvKxiuX" name="Nespresso_Vertuo_Up 1.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/a8urLUZzPQsRu4XVvKxiuX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As this is a Vertuo machine, replacement pods are only available from Nespresso’s official Amazon store and Nespresso direct. This is disappointing. I wanted to buy some extra pods and visited my local grocery store (a large store with tons of products, including Nespresso pods), but it only stocked original pods. After a quick Google, I discovered that I could only buy these specific pods online. </p><p>Therefore, you’ll need some pre-planning to ensure you don’t run out of pods. The price of pods is also quite different from original Nespresso pods. A 30-pack of OG Nespresso pods retails for $27, working out to $0.90 per pod. Vertuo pods are $40.50 for the same sized pack, $1.35 per pod. The pods I personally tested with were around $1.25-$1.55 per pod. </p><p>So while the Vertuo machines are cheaper from the off, you might actually spend more money over time. Let’s quickly do some coffee math. Say you’re spending $13 extra a month (if you drink one a day). There’s a $36 price difference between the Vertuo Up and the Citiz. After 3 months, you’ll have spent that extra $36 on Nespresso’s patented Vertuo pods. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gb23XMYiL2ueRX7hT2ApuX" name="Nespresso_Vertuo_Up 8.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/gb23XMYiL2ueRX7hT2ApuX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Personally, I would always buy an original Nespresso machine for this reason, but of course, the decision is up to you. </p><p>Cleaning the machine is straightforward: there are detailed instructions in the app and you can start cleaning cycles from your phone. You can also buy cleaning kits directly from the app: a descaling kit is $10.99 for two sachets. </p><p>Alright, last but not least: pod recycling. Of course, not buying single-use pods is inherently more environmentally friendly than buying single-use pods, but at least you can recycle Nespresso pods. You can either get a recycling bag direct from Nespresso, return your pods to Nespresso stores, or drop off your pods at select convenience stores. </p><h2 class="article-body__section" id="section-nespresso-vertuo-up-review-verdict"><span>Nespresso Vertuo Up review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rWaXwVXAGNU9MbN3Uxj5wX" name="Nespresso_Vertuo_Up 10.JPG" alt="the nespresso vertuo up machine photographed against a blue background" src="https://cdn.mos.cms.futurecdn.net/rWaXwVXAGNU9MbN3Uxj5wX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Nespresso Vertuo Up is a really great Nespresso machine. It’s beyond easy to use, looks pretty good, and the app is genuinely useful. While it’s not perfect, hence the 4-star rating, it’s bound to make any hands-free coffee lover happy. My colleagues all remarked on its ease of use and pleasant coffee flavor. </p><p>Sure, I’d appreciate some on-machine controls for the next gen and I’d appreciate more pod options, but if you want to save that initial cost with a Vertuo, this is a pretty good one to get. It can make iced and hot coffees in seconds, and never made a bad brew (except my experiment). </p><p>I recommend the Nespresso Vertuo Up for anyone interested in upgrading an old Nespresso machine, anyone who particularly likes having smart control features on their coffee machine, or, simply, anyone who wants hot (or cold) coffee and wants it now. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tried the new Nespresso summer flavors — here's my favorite ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the fifteenth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>Nespresso always seems to be cooking up something exciting. So what's happening this summer 2026? Not only have we got the brand-new <a href="https://tomsguide.com/home/coffee-makers/nespresso-vertuo-up-review">Nespresso Vertuo Up</a>, one of the <a href="https://www.tomsguide.com/best-picks/best-nespresso-machine">best Nespresso machines</a>, we've also got...</p><p>Summer Nespresso flavors! I tried three of the new flavors — Yuzu & Vanilla Over Ice, Coconut Vanilla Over Ice, and Oatly Barista Edition. I didn't get a chance to try out the new decaf French Lavender & Vanilla, but this is a super fun flavor. Usually, decaf drinkers don't get as many options, so it's great to see Nespresso remember our non-caffeinated brethren. </p><p>So, which of the new Nespresso summer flavors is worth it? Does it actually taste like yuzu? Don't fear, because I'll answer all your questions and more. I made all of these drinks on the new Nespresso Vertuo Up.</p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7660886548849642783" data-video-id="7660886548849642783" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ Pursue what you like - LMS" href="https://www.tiktok.com/music/Pursue-what-you-like-7400652005268817937">♬ Pursue what you like - LMS</a></section>                    </blockquote></div>                <h2 id="flavor-1-yuzu-vanilla-over-ice">Flavor 1: Yuzu & Vanilla Over Ice</h2><p>First up is the one I'm most excited for: the <strong>Yuzu & Vanilla Over Ice</strong>. This pod makes 80ml, a "double espresso", and I served it both black and with milk. While milk brought out a creamy, almost cheesecake-like flavor, just water tasted light and clean.</p><div class="product"><a data-dimension112="bba621c6-79fd-11f1-93d8-d7253e29e454" data-action="Deal Block" data-label="Yuzu & Vanilla Over Ice" data-dimension48="Yuzu & Vanilla Over Ice" data-dimension25="$1.55" href="https://www.nespresso.com/us/en/order/capsules/vertuo/yuzu-vanilla-coffee" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="2CSBr5pKDQzPB6VLvhuetC" name="nespresso yuzu pod" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/2CSBr5pKDQzPB6VLvhuetC.png" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Yuzu & vanilla over ice combines Brazilian and Colombian light-roast coffee with a delicate twist of fruity Japanese yuzu to make a refreshing, clean-tasting cup.<a class="view-deal button" href="https://www.nespresso.com/us/en/order/capsules/vertuo/yuzu-vanilla-coffee" target="_blank" rel="nofollow" data-dimension112="bba621c6-79fd-11f1-93d8-d7253e29e454" data-action="Deal Block" data-label="Yuzu & Vanilla Over Ice" data-dimension48="Yuzu & Vanilla Over Ice" data-dimension25="$1.55">View Deal</a></p></div><p>The first photo is black, the second is with oat milk.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Cjh4KwZZkksQHwuVjFk3yM.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/VCty5TSsiKbSPQwgvCRspY.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin</small></figcaption></figure></figure><p><strong>The flavor</strong>: The yuzu flavor is really subtle. If you're worried about it being a wall of citrus, don't be. The coffee, vanilla, and yuzu notes are complementary, rather than battling one another for dominance. It tastes more like a coffee with a whisper of lemon than anything else.</p><p>If you like juicy, fruity drinks, like African coffee beans or Starbucks Pink Drink, you'll probably like this a lot. </p><h2 id="flavor-2-coconut-vanilla-over-ice">Flavor 2: Coconut & Vanilla Over Ice</h2><p>Next is <strong>Coconut & Vanilla Over Ice</strong>. This pod is a little different as it makes a 230ml "coffee" rather than an 80ml "double espresso". You don't need to add anything except ice. </p><div class="product"><a data-dimension112="bba62400-79fd-11f1-96c5-1f44f3be71fa" data-action="Deal Block" data-label="Coconut & Vanilla Over Ice" data-dimension48="Coconut & Vanilla Over Ice" data-dimension25="$1.60" href="https://www.nespresso.com/us/en/order/capsules/vertuo/coconut-vanilla-coffee" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="TWKPZiP2vFs5ytyhjU3xv5" name="coconut vanilla nespresso" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/TWKPZiP2vFs5ytyhjU3xv5.png" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Coconut & vanilla over ice is a long coffee best served black. Nespresso used a mix of Latin American beans with a delicate vanilla sweetness to balance out a nutty, toasty coconut taste profile. <a class="view-deal button" href="https://www.nespresso.com/us/en/order/capsules/vertuo/coconut-vanilla-coffee" target="_blank" rel="nofollow" data-dimension112="bba62400-79fd-11f1-96c5-1f44f3be71fa" data-action="Deal Block" data-label="Coconut & Vanilla Over Ice" data-dimension48="Coconut & Vanilla Over Ice" data-dimension25="$1.60">View Deal</a></p></div><p>Here's a photo of the Nespresso Coconut & Vanilla Over Ice.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4182px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="7H3a3gL644uxDxY6mYepCP" name="nespresso coconut" alt="a coffee made on the nespresso vertuo up" src="https://cdn.mos.cms.futurecdn.net/7H3a3gL644uxDxY6mYepCP.png" mos="" align="middle" fullscreen="" width="4182" height="2352" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p><strong>The flavor</strong>: Just like the Nespresso Yuzu & Vanilla Over Ice, Coconut & Vanilla is subtle and delicate. You might not even know it's supposed to be vanilla flavored without reading the packet. I like the caramelly mouthfeel — the drink is smooth and warming even though it's completely iced — and the coconut does well to round out the coffee. </p><p>I'd say this tastes more like an iced batch brew with a little brown sugar than a coconut milk coffee. I was worried it would be too coconutty, but it's not. This coffee is better for people who want a subtle toasty flavor rather than a coconut milk latte. I really enjoyed it, but I think I would prefer a bolder coconut taste.</p><h2 id="flavor-3-oatly-barista-edition">Flavor 3: Oatly Barista Edition</h2><p>As an oat milk fanatic, I was surprised and pleased to see an <strong>Oatly Barista Edition Nespresso</strong> pod. Those who know me will know I go through a carton of the stuff every few days. </p><p>This pod brews an 80ml "double shot" and is designed to be made iced. </p><div class="product"><a data-dimension112="bba6263a-79fd-11f1-af2a-7b55fc95bb35" data-action="Deal Block" data-label="Oatly Barista Edition" data-dimension48="Oatly Barista Edition" data-dimension25="$1.55" href="https://www.nespresso.com/us/en/order/capsules/vertuo/oatly-barista-edition" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:382px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="xvyKSPmaNKmjgpDLqGmgR8" name="oatly barista nespresso" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/xvyKSPmaNKmjgpDLqGmgR8.png" mos="" align="middle" fullscreen="" width="382" height="382" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Oatly Barista Edition Nespresso pod uses coffee from Nicaragua and El Salvador, the former roasted lightly and the latter dark-roasted for a malty, nutty, almost earthy flavor. <a class="view-deal button" href="https://www.nespresso.com/us/en/order/capsules/vertuo/oatly-barista-edition" target="_blank" rel="nofollow" data-dimension112="bba6263a-79fd-11f1-af2a-7b55fc95bb35" data-action="Deal Block" data-label="Oatly Barista Edition" data-dimension48="Oatly Barista Edition" data-dimension25="$1.55">View Deal</a></p></div><p>Here are two photos of the Oatly Barista Edition over ice. The first photo uses oat milk and the second was made with dairy milk.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Yt7hsorFdn52WjbPv7kFf5.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/iMJBANgsRAb89r4ka9ykEP.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure></figure><p><strong>The flavor</strong>: This was my favorite of the three by far. This pod has a strong, malty coffee flavor. The beans are rich and caramelly with zero bitterness. There's no whisper of yuzu or coconut. It tastes like coffee — in the best way possible. </p><p>This would be the best pod if you like flat whites or cortados.</p><h2 id="my-rankings">My rankings</h2><p>Alright, after much deliberation, I've finally settled on my Nespresso pod ranking.</p><ol start="1"><li>Oatly Barista Edition</li><li>Yuzu & Vanilla</li><li>Coconut & Vanilla</li></ol><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-this-ridiculously-cheap-bean-to-cup-espresso-machine-so-you-dont-have-to-and-its-a-total-head-scratcher"><strong>I tried this ridiculously cheap bean-to-cup espresso machine so you don't have to — and it's a total head-scratcher</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/non-plastic-coffee-makers-are-trending-and-this-low-plastic-ratio-model-is-my-favorite-for-coffee-snobs"><strong>Non-plastic coffee makers are trending — and this low-plastic Ratio model is my favorite for coffee snobs</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/coffee-makers/i-tried-the-new-nespresso-summer-flavors-heres-my-favorite</link>
                                                                            <description>
                            <![CDATA[ On the 15th episode of The Coffee Lab, I'm trying all of the new Nespresso summer flavors. Which one will be my favorite? ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">44ZTTFBWaGuCjyCxhw7Vm6</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/ysCNduzcQ8bBkCwv74xZy5-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Fri, 10 Jul 2026 10:00:00 +0000</pubDate>                                                                                                                                <updated>Wed, 15 Jul 2026 09:59:29 +0000</updated>
                                                                                                                                            <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen & Dining]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/png" url="https://cdn.mos.cms.futurecdn.net/ysCNduzcQ8bBkCwv74xZy5-1280-80.png">
                                                            <media:credit><![CDATA[Tom&#039;s Guide / Nespresso via Instagram]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[a side by side image of the nespresso vertuo up wih summer flavors and the nespresso images]]></media:description>                                                            <media:text><![CDATA[a side by side image of the nespresso vertuo up wih summer flavors and the nespresso images]]></media:text>
                                <media:title type="plain"><![CDATA[a side by side image of the nespresso vertuo up wih summer flavors and the nespresso images]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/ysCNduzcQ8bBkCwv74xZy5-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">The Coffee Lab</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UwFdgr6xkNzHUWjapZThcN" name="smeg edited2" caption="" alt="the smeg emc02 mini pro manual espresso machine in jade green" src="https://cdn.mos.cms.futurecdn.net/UwFdgr6xkNzHUWjapZThcN.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text">I'm Erin, and welcome to the fifteenth episode of <a data-analytics-id="inline-link" href="https://www.tomsguide.com/tag/the-coffee-lab">The Coffee Lab</a>, the series where we forget coffee snobbery. The Coffee Lab is all about making coffee fun. Join me as I help you kickstart your coffee journey!</p></div></div><p>Nespresso always seems to be cooking up something exciting. So what's happening this summer 2026? Not only have we got the brand-new <a href="https://tomsguide.com/home/coffee-makers/nespresso-vertuo-up-review">Nespresso Vertuo Up</a>, one of the <a href="https://www.tomsguide.com/best-picks/best-nespresso-machine">best Nespresso machines</a>, we've also got...</p><p>Summer Nespresso flavors! I tried three of the new flavors — Yuzu & Vanilla Over Ice, Coconut Vanilla Over Ice, and Oatly Barista Edition. I didn't get a chance to try out the new decaf French Lavender & Vanilla, but this is a super fun flavor. Usually, decaf drinkers don't get as many options, so it's great to see Nespresso remember our non-caffeinated brethren. </p><p>So, which of the new Nespresso summer flavors is worth it? Does it actually taste like yuzu? Don't fear, because I'll answer all your questions and more. I made all of these drinks on the new Nespresso Vertuo Up.</p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7660886548849642783" data-video-id="7660886548849642783" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@tomsguide" href="https://www.tiktok.com/@tomsguide">@tomsguide</a>                            <p></p><a target="_blank" title="♬ Pursue what you like - LMS" href="https://www.tiktok.com/music/Pursue-what-you-like-7400652005268817937">♬ Pursue what you like - LMS</a></section>                    </blockquote></div>                <h2 id="flavor-1-yuzu-vanilla-over-ice">Flavor 1: Yuzu & Vanilla Over Ice</h2><p>First up is the one I'm most excited for: the <strong>Yuzu & Vanilla Over Ice</strong>. This pod makes 80ml, a "double espresso", and I served it both black and with milk. While milk brought out a creamy, almost cheesecake-like flavor, just water tasted light and clean.</p><div class="product"><a data-dimension112="bba621c6-79fd-11f1-93d8-d7253e29e454" data-action="Deal Block" data-label="Yuzu & Vanilla Over Ice" data-dimension48="Yuzu & Vanilla Over Ice" data-dimension25="$1.55" href="https://www.nespresso.com/us/en/order/capsules/vertuo/yuzu-vanilla-coffee" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="2CSBr5pKDQzPB6VLvhuetC" name="nespresso yuzu pod" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/2CSBr5pKDQzPB6VLvhuetC.png" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Yuzu & vanilla over ice combines Brazilian and Colombian light-roast coffee with a delicate twist of fruity Japanese yuzu to make a refreshing, clean-tasting cup.<a class="view-deal button" href="https://www.nespresso.com/us/en/order/capsules/vertuo/yuzu-vanilla-coffee" target="_blank" rel="nofollow" data-dimension112="bba621c6-79fd-11f1-93d8-d7253e29e454" data-action="Deal Block" data-label="Yuzu & Vanilla Over Ice" data-dimension48="Yuzu & Vanilla Over Ice" data-dimension25="$1.55">View Deal</a></p></div><p>The first photo is black, the second is with oat milk.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Cjh4KwZZkksQHwuVjFk3yM.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/VCty5TSsiKbSPQwgvCRspY.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin</small></figcaption></figure></figure><p><strong>The flavor</strong>: The yuzu flavor is really subtle. If you're worried about it being a wall of citrus, don't be. The coffee, vanilla, and yuzu notes are complementary, rather than battling one another for dominance. It tastes more like a coffee with a whisper of lemon than anything else.</p><p>If you like juicy, fruity drinks, like African coffee beans or Starbucks Pink Drink, you'll probably like this a lot. </p><h2 id="flavor-2-coconut-vanilla-over-ice">Flavor 2: Coconut & Vanilla Over Ice</h2><p>Next is <strong>Coconut & Vanilla Over Ice</strong>. This pod is a little different as it makes a 230ml "coffee" rather than an 80ml "double espresso". You don't need to add anything except ice. </p><div class="product"><a data-dimension112="bba62400-79fd-11f1-96c5-1f44f3be71fa" data-action="Deal Block" data-label="Coconut & Vanilla Over Ice" data-dimension48="Coconut & Vanilla Over Ice" data-dimension25="$1.60" href="https://www.nespresso.com/us/en/order/capsules/vertuo/coconut-vanilla-coffee" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="TWKPZiP2vFs5ytyhjU3xv5" name="coconut vanilla nespresso" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/TWKPZiP2vFs5ytyhjU3xv5.png" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Coconut & vanilla over ice is a long coffee best served black. Nespresso used a mix of Latin American beans with a delicate vanilla sweetness to balance out a nutty, toasty coconut taste profile. <a class="view-deal button" href="https://www.nespresso.com/us/en/order/capsules/vertuo/coconut-vanilla-coffee" target="_blank" rel="nofollow" data-dimension112="bba62400-79fd-11f1-96c5-1f44f3be71fa" data-action="Deal Block" data-label="Coconut & Vanilla Over Ice" data-dimension48="Coconut & Vanilla Over Ice" data-dimension25="$1.60">View Deal</a></p></div><p>Here's a photo of the Nespresso Coconut & Vanilla Over Ice.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4182px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="7H3a3gL644uxDxY6mYepCP" name="nespresso coconut" alt="a coffee made on the nespresso vertuo up" src="https://cdn.mos.cms.futurecdn.net/7H3a3gL644uxDxY6mYepCP.png" mos="" align="middle" fullscreen="" width="4182" height="2352" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p><strong>The flavor</strong>: Just like the Nespresso Yuzu & Vanilla Over Ice, Coconut & Vanilla is subtle and delicate. You might not even know it's supposed to be vanilla flavored without reading the packet. I like the caramelly mouthfeel — the drink is smooth and warming even though it's completely iced — and the coconut does well to round out the coffee. </p><p>I'd say this tastes more like an iced batch brew with a little brown sugar than a coconut milk coffee. I was worried it would be too coconutty, but it's not. This coffee is better for people who want a subtle toasty flavor rather than a coconut milk latte. I really enjoyed it, but I think I would prefer a bolder coconut taste.</p><h2 id="flavor-3-oatly-barista-edition">Flavor 3: Oatly Barista Edition</h2><p>As an oat milk fanatic, I was surprised and pleased to see an <strong>Oatly Barista Edition Nespresso</strong> pod. Those who know me will know I go through a carton of the stuff every few days. </p><p>This pod brews an 80ml "double shot" and is designed to be made iced. </p><div class="product"><a data-dimension112="bba6263a-79fd-11f1-af2a-7b55fc95bb35" data-action="Deal Block" data-label="Oatly Barista Edition" data-dimension48="Oatly Barista Edition" data-dimension25="$1.55" href="https://www.nespresso.com/us/en/order/capsules/vertuo/oatly-barista-edition" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:382px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="xvyKSPmaNKmjgpDLqGmgR8" name="oatly barista nespresso" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/xvyKSPmaNKmjgpDLqGmgR8.png" mos="" align="middle" fullscreen="" width="382" height="382" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Oatly Barista Edition Nespresso pod uses coffee from Nicaragua and El Salvador, the former roasted lightly and the latter dark-roasted for a malty, nutty, almost earthy flavor. <a class="view-deal button" href="https://www.nespresso.com/us/en/order/capsules/vertuo/oatly-barista-edition" target="_blank" rel="nofollow" data-dimension112="bba6263a-79fd-11f1-af2a-7b55fc95bb35" data-action="Deal Block" data-label="Oatly Barista Edition" data-dimension48="Oatly Barista Edition" data-dimension25="$1.55">View Deal</a></p></div><p>Here are two photos of the Oatly Barista Edition over ice. The first photo uses oat milk and the second was made with dairy milk.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Yt7hsorFdn52WjbPv7kFf5.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/iMJBANgsRAb89r4ka9ykEP.png" alt="a coffee made on the nespresso vertuo up" /><figcaption><small role="credit">Erin Bashford</small></figcaption></figure></figure><p><strong>The flavor</strong>: This was my favorite of the three by far. This pod has a strong, malty coffee flavor. The beans are rich and caramelly with zero bitterness. There's no whisper of yuzu or coconut. It tastes like coffee — in the best way possible. </p><p>This would be the best pod if you like flat whites or cortados.</p><h2 id="my-rankings">My rankings</h2><p>Alright, after much deliberation, I've finally settled on my Nespresso pod ranking.</p><ol start="1"><li>Oatly Barista Edition</li><li>Yuzu & Vanilla</li><li>Coconut & Vanilla</li></ol><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="ojE0pK" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-this-ridiculously-cheap-bean-to-cup-espresso-machine-so-you-dont-have-to-and-its-a-total-head-scratcher"><strong>I tried this ridiculously cheap bean-to-cup espresso machine so you don't have to — and it's a total head-scratcher</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/non-plastic-coffee-makers-are-trending-and-this-low-plastic-ratio-model-is-my-favorite-for-coffee-snobs"><strong>Non-plastic coffee makers are trending — and this low-plastic Ratio model is my favorite for coffee snobs</strong></a></li><li><a href="https://www.tomsguide.com/home/coffee-makers/i-tried-lelits-brand-new-entry-level-espresso-machine-and-i-wish-i-had-usd2-000-so-i-could-buy-it"><strong>I tried Lelit's brand-new 'entry-level' espresso machine — and I wish I had $2,000 so I could buy it</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Forget fad appliances — I’ve found 24 Prime Day discounts on kitchen accessories & utensils that will streamline your culinary creations ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I love my kitchen, but there are some days when I want to replace every single utensil I own. I'm talking mismatched cutlery and crockery, a hard-to-clean stainless steel pan, and some ageing kitchen scales that have served me and my baking skills well over the last few years. </p><p>Upgrades like these are ones I've been putting off, simply because I don't have the means to replace everything. And I'm sure most of us would agree that it's easier to shove an old can opener or chipped plate back into the kitchen drawer than try to find a replacement. </p><p>Thankfully, it's <a href="https://www.tomsguide.com/live/news/amazon-prime-day-2026-au" target="_blank">Prime Day</a>, and I've found 24 discounts on kitchen accessories, utensils and small appliances that will upgrade your kitchen for under AU$150 on Amazon. Granted, there's one item on this list — <a href="https://www.amazon.com.au/dp/B07QCD5QX2/ref=sspa_dk_detail_0" target="_blank">namely, the Corelle Portofino 18-piece dinner set</a> — that is a bit above this price, at AU$159.20; however, I've had my eye on it for a while, and it's finally within my reach at AU$40 off RRP. And for an 18-piece set, that's only AU$8.80 per piece, which is a big win in my books. </p><p>Another great deal I've spotted is on <a href="https://www.amazon.com.au/Shark-Cordfree-Handheld-WV203-Graphite/dp/B07X82QCY7/" target="_blank">Shark's Cordfree Handheld vacuum, now AU$135.85</a>. While it may be a bit odd to put on a kitchen deals round up, I swear by my handheld vacuum for all the teeny crumbs and coffee grinds in my kitchen. Trust me, it is <em>life-changing</em>. </p><p>Don't just take my word for it, though — all these products come with 4+ star ratings, including those of my colleagues at Tom's Guide. Better yet, these deals are exclusive to Prime members, and if you're not signed up already, you can get a <a href="https://www.amazon.com.au/tryprimefree" target="_blank">free 30-day trial here</a>. You'll need to act fast to nab any of these kitchen deals, though, as they will expire on <strong>Monday, July 13, at 11:59pm. </strong></p>        <div class="featured_product_block featured_block_hero" data-id="ee965c88-7c08-11f1-a318-d1b673bf5819">            <a href="https://www.amazon.com.au/Wireless-Thermometer-INT-11I-B-Bluetooth-Waterproof/dp/B0F3XHR5Q6" data-model-name="Mini Wireless Meat Thermometer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/GDpGqAWeoHB669CnbKsU7g.jpg" alt="Inkbird Mini Wireless Meat Thermometer Int-11i-B Bluetooth 5.4 Bbq Food Probe Ip67 Waterproof Led Display for Bbq Grill Oven Smoker Outdoor Camping Kitchen"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Inkbird</div>                                        <div class="featured__title">Mini Wireless Meat Thermometer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965cf6-7c08-11f1-b53b-6937dc3e2575">            <a href="https://www.amazon.com.au/BRITA-Filter-PRO-Performance-Cartridge/dp/B0D7M7ZCJX" data-model-name="Flow Cask Water Filter Dispenser" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/yKfpnyJveQosy5QQgiKUsa.jpg" alt="Brita Flow Cask Water Filter Dispenser, Blue, 8.2l Capacity, Bpa-Free With Tap and 1 Maxtra Pro Cartridge"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>BRITA</div>                                        <div class="featured__title">Flow Cask Water Filter Dispenser</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965d64-7c08-11f1-9619-7548055619ba">            <a href="https://www.amazon.com.au/Automatic-Preservation-Cleaning-Stainless-Indicator/dp/B08LCXZYLK" data-model-name="Vacuum Sealer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:85.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/b9LpQumfcZSCNmy4Hrs7xa.jpg" alt="Inkbird Vacuum Sealer Automatic Sealing Machine for Food Preservation Dry Moist Sealing Modes With Built-In Cutter Starter Kit Easy Cleaning Stainless Steel Panel Compact Design Led Indicator Lights"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Inkbird</div>                                        <div class="featured__title">Vacuum Sealer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965dbe-7c08-11f1-a070-61669ed4fc92">            <a href="https://www.amazon.com.au/AmazonBasics-Stainless-Digital-Batteries-Included/dp/B06X9NQ8GX/" data-model-name="Digital Kitchen Scale With LCD Display" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jD5qV6iEvWHnumfLsaTnFg.jpg" alt="Amazon Basics Digital Kitchen Scale With Lcd Display, Batteries Included, Weighs Up to 4.9 Kgs, Black and Stainless Steel"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon Basics</div>                                        <div class="featured__title">Digital Kitchen Scale With LCD Display</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_standard" data-id="ee965e22-7c08-11f1-bc5a-5daa4a14db74">            <a href="https://www.amazon.com.au/Shark-Cordfree-Handheld-WV203-Graphite/dp/B07X82QCY7/" data-model-name="Cordfree Handheld Vacuum" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/xdPTMjpXZRrHy3vujHFYKW.jpg" alt="Shark Cordfree Handheld Vacuum, Ultra-Lightweight and Portable With Powerful Suction & Tools for Pets, Designed for Car and Home, Grey, Wv203"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shark</div>                                        <div class="featured__title">Cordfree Handheld Vacuum</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_standard" data-id="ee965e86-7c08-11f1-8825-6746df393c77">            <a href="https://www.amazon.com.au/CIRCLE-JOY-Rechargeable-Cappuccino-Chocolate/dp/B0DPG6SNV1/" data-model-name="Double Whisk Milk Frother" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/JWecjk8LDzWwGR8MjkDYNW.jpg" alt="Circle Joy Powerful Double Whisk Milk Frother Handheld Rechargeable Milk Foamer Coffee Frother Drink Mixer With Cover, Portable for Coffee, Cappuccino, Lattes, Frappe, Matcha and Hot Chocolate, Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>CIRCLE JOY</div>                                        <div class="featured__title">Double Whisk Milk Frother</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965eea-7c08-11f1-b50a-df232d73edc1">            <a href="https://www.amazon.com.au/Corelle-18-Piece-Dinnerware-Resistant-Lightweight/dp/B0119IOPIY" data-model-name="Dinnerware Set, Country Cottage (18-Piece Set)" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/sa8hsWZmrjDRGWinKRyyAb.jpg" alt="Corelle Dinnerware Set, Country Cottage (18-Piece Set)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>CORELLE</div>                                        <div class="featured__title">Dinnerware Set, Country Cottage (18-Piece Set)</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965f44-7c08-11f1-bc9d-3f2737d0db5b">            <a href="https://www.amazon.com.au/AmazonBasics-Enameled-Cast-Iron-Dutch/dp/B073Q9K2H3" data-model-name="Enamelled Cast Iron Covered Round Dutch Oven" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/f2rA3bavQLLoYE2s6aUCQb.jpg" alt="Amazon Basics Enameled Cast Iron Covered Round Dutch Oven, 5.7 Liters / 6 Quart, Red"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon Basics</div>                                        <div class="featured__title">Enamelled Cast Iron Covered Round Dutch Oven</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965fa8-7c08-11f1-b660-97e8fd267227">            <a href="https://www.amazon.com.au/Pyrex-Fryer-Glass-Baking-Piece/dp/B0DK38BPP7/" data-model-name="Air Fryer Glass Baking Cups 4 Piece Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/WPrByfmBrYZ52YYx7LaD2b.jpg" alt="Pyrex Air Fryer Glass Baking Cups 4 Piece Set – Borosilicate Glass Bakeware and Silicone Grip Pad – Microwave, Oven, Air Fryer, Freezer & Dishwasher Safe"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Pyrex</div>                                        <div class="featured__title">Air Fryer Glass Baking Cups 4 Piece Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966002-7c08-11f1-8d1b-f727d3c6c704">            <a href="https://www.amazon.com.au/Tefal-Non-Stick-C2890402-Aluminium-Technology/dp/B0CDQFTXZT" data-model-name="Daily Expert Red Non-Stick Frypan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/KqSK9KsxMBGJpwKc5h2iJb.jpg" alt="Tefal Daily Expert Red Non-Stick Frypan, 24cm, C2890402, Fixed Handle Aluminium, Titanium Non-Stick Coating, Thermo Signal Technology for Easier Cooking, Suitable for All Cooktops, Oven Safe"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Tefal</div>                                        <div class="featured__title">Daily Expert Red Non-Stick Frypan</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee96605c-7c08-11f1-8914-1921d3c7160e">            <a href="https://www.amazon.com.au/dp/B07QCD5QX2/ref=sspa_dk_detail_0" data-model-name="Portofino 18 Piece Dinnerware Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/zui8FFA2tqr9EogiBbd57b.jpg" alt="Corelle Portofino 18 Piece Dinnerware Set – Lightweight and Chip-Resistant Glass – Microwave and Dishwasher Safe – 6 X 26cm Dinner Plate, 6 X 17cm Side Plate, 4 X 532ml Soup/cereal Bowl"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>CORELLE</div>                                        <div class="featured__title">Portofino 18 Piece Dinnerware Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee9660c0-7c08-11f1-a70c-03556db61140">            <a href="https://www.amazon.com.au/Scanpan-Impact-Saucepan-Quality-Stainless/dp/B075LDQZMQ/" data-model-name="Impact 3-Piece Stainless Steel Saucepan Set With Tempered Glass Lids" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:93.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/RvFDeAUyTYH6Jcqc9UzH4g.jpg" alt="Scanpan Impact 3-Piece Stainless Steel Saucepan Set With Tempered Glass Lids,silver"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Scanpan</div>                                        <div class="featured__title">Impact 3-Piece Stainless Steel Saucepan Set With Tempered Glass Lids</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee96611a-7c08-11f1-8c4d-7be995c89c14">            <a href="https://www.amazon.com.au/Tefal-Non-Stick-Indicator-Recycled-G2933202/dp/B0D87HTGR6" data-model-name="Delight Sauté Pan, non stick" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:62.80%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/hxzNAHZaVGPyD47nrnPhmf.jpg" alt="Tefal Delight Sauté Pan 24 Cm, Non-Stick Coating, Heat Indicator, 100% Recycled Aluminum Body, G2933202"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Tefal</div>                                        <div class="featured__title">Delight Sauté Pan, non stick</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966174-7c08-11f1-8ee0-559a039e5c40">            <a href="https://www.amazon.com.au/Joseph-4-piece-Chopping-Storage-Regular/dp/B07YGJMNVK" data-model-name="Folio 4-Piece Chopping Board Set With Storage Case" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:75.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/VBUaFWiKupvKdmyTh95oHb.jpg" alt="Joseph Joseph Folio 4-Piece Chopping Board Set With Storage Case (regular) - Graphite"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Folio 4-Piece Chopping Board Set With Storage Case</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee9661d8-7c08-11f1-a85c-0b6a48335e4e">            <a href="https://www.amazon.com.au/GreenLife-Artisan-Healthy-Ceramic-Nonstick/dp/B098FGB5XC" data-model-name="Artisan Healthy Ceramic Nonstick Frying Pan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ddGTw2viSjbAKQsKj6BwLb.jpg" alt="Greenlife Artisan Healthy Ceramic Nonstick, Frying Pan/skillet Set, 8" and 10", Yellow"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>GreenLife</div>                                        <div class="featured__title">Artisan Healthy Ceramic Nonstick Frying Pan</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966232-7c08-11f1-ae8f-4d12651e9b9b">            <a href="https://www.amazon.com.au/Pyrex-Deep-Baking-6-Piece-BPA-Free/dp/B07K14QNCC" data-model-name="Deep Dish Glass Baking Dishes With Sage Lids" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/tqCYcGNY6qZ4ahtWMNbVjf.jpg" alt="Pyrex 1135014 Deep Dish Glass Baking Dishes With Sage Lids – Extra Deep Design and Durable Tempered Glass – Dishwasher, Microwave, Freezer Safe – 6 Piece Set"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Pyrex</div>                                        <div class="featured__title">Deep Dish Glass Baking Dishes With Sage Lids</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966296-7c08-11f1-b321-ff9e9676d1b8">            <a href="https://www.amazon.com.au/Stainless-Steel-Griddle-Scraper-Chopper/dp/B072MTB9H8/" data-model-name="Stainless Steel Metal Griddle Scraper" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rzBsWFaevc6g3xWBEGPEgL.jpg" alt="Stainless Steel Metal Griddle Scraper Chopper - Great as Dough Cutter for Bread and Pizza Dough - Multipurpose Kitchen Utensil for Flat Top Griddle - Commercial Quality"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Anmarko</div>                                        <div class="featured__title">Stainless Steel Metal Griddle Scraper</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee9662f0-7c08-11f1-a6b4-b7367ced3f80">            <a href="https://www.amazon.com.au/Joseph-Editions-9-piece-compact-preparation/dp/B07TT79CYC/" data-model-name="Editions Nest 9 Plus Preparation set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/pfubmd2GqJaXFrTyLnndcL.jpg" alt="Joseph Joseph Editions Nest 9 Plus, 9-Piece Compact Food Preparation Set - Sky"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Editions Nest 9 Plus Preparation set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966354-7c08-11f1-89cd-7fb9b45d7ac7">            <a href="https://www.amazon.com.au/Wiltshire-Bakeware-Smart-Stack-Rose/dp/B0B65T2D6Z/" data-model-name=" 7 Piece Smart Stack Bakeware Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:140.45%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ez74US7CH2egDafsuQMvkL.jpg" alt="Wiltshire 7 Piece Smart Stack Bakeware Set Rose Gold – Non-Stick Baking Trays, Cake Tins, Muffin Pan and Roasting Pan. Space-Saving Storage Solution in Gift Box"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Wiltshire</div>                                        <div class="featured__title"> 7 Piece Smart Stack Bakeware Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee9663ae-7c08-11f1-be36-63127958b76e">            <a href="https://www.amazon.com.au/Joseph-Scoop-Plus-Colander-Large/dp/B07FMB4RN8/" data-model-name="Scoop Plus " data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:54.40%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/FyL8FBNheXdotvZWKHhdaL.jpg" alt="Joseph Joseph Scoop Plus Large - Grey/green"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Scoop Plus </div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966408-7c08-11f1-80c7-211da0ab45c4">            <a href="https://www.amazon.com.au/JOSEPH-20101-Juicer-Citrus-Yellow/dp/B01N2Y8QK1" data-model-name="Helix Citrus Press" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:62.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/N47YaJSC3xzpHQpWCL6iWL.jpg" alt="Joseph Joseph Helix Citrus Press,yellow and Green"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Helix Citrus Press</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee96646c-7c08-11f1-bc10-f7ecfcfea2b3">            <a href="https://www.amazon.com.au/D%C3%A9cor-Cook-Refillable-Oil-Sprayer/dp/B07C5S3LHD" data-model-name="Refillable Oil Sprayer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:150%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/qWbtL8UkZj69VUjAo2Lzen.jpg" alt="Décor Décor Cook Refillable Oil Sprayer,white"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Décor</div>                                        <div class="featured__title">Refillable Oil Sprayer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee9664c6-7c08-11f1-92c1-63f875c81c00">            <a href="https://www.amazon.com.au/Magnetic-Measuring-Stainless-Stackable-Ingredients/dp/B09W9Z52B2" data-model-name="Magnetic Measuring Spoons Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/hgJEsk8hgJXxSiBhHZTGiL.jpg" alt="Magnetic Measuring Spoons Set, 7 Measuring Spoons and Cups 1 Leveler Stainless Steel Dual Sided Stackable Teaspoon for Liquid and Dry Ingredients"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Hushnow</div>                                        <div class="featured__title">Magnetic Measuring Spoons Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966520-7c08-11f1-bea9-6b7dff875bd3">            <a href="https://www.amazon.com.au/Amazon-Basics-Baking-Bread-24-13/dp/B073P52PPR" data-model-name="Baking Bread Loaf Pan, 2pk" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:57.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/QhRNuojdFRcW24A9Bxknid.jpg" alt="Amazon Basics Baking Bread Loaf Pan, 24.1 X 12.7 Cm, Set of 2"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon Basics</div>                                        <div class="featured__title">Baking Bread Loaf Pan, 2pk</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/kitchen-dining/forget-fad-appliances-ive-found-24-prime-day-discounts-on-kitchen-accessories-and-utensils-that-will-streamline-your-culinary-creations</link>
                                                                            <description>
                            <![CDATA[ Looking to upgrade your kitchen for less? I've found some top-rated kitchen accessories that are on sale for Prime Day — and they're all under AU$150. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">9NsC49bjCP9JQVpix39oTk</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/C6yXKeqm2Lhdhh9Qq68X8a-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 10 Jul 2026 03:42:56 +0000</pubDate>                                                                                                                                <updated>Fri, 10 Jul 2026 04:18:01 +0000</updated>
                                                                                                                                            <category><![CDATA[Kitchen & Dining]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                                                                <author><![CDATA[ lucy.scotting@futurenet.com (Lucy Scotting) ]]></author>                    <dc:creator><![CDATA[ Lucy Scotting ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/UC9ncAYxkmJ5ipHEyX44ri.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Lucy Scotting is a staff writer for Tom’s Guide Australia, primarily covering lifestyle, streaming and internet-related news. Lucy started her career writing for HR and staffing industry publications, with articles covering emerging tech, business and finance.&lt;/p&gt;&lt;p&gt;In her spare time, Lucy can be found watching sci-fi movies, working on her dystopian fiction novel (in progress since 2017) or hanging out with her dog, Fletcher. If she’s not found doing any of the above, she’s likely on her next adventure to a new city, country, or continent. &lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/C6yXKeqm2Lhdhh9Qq68X8a-1280-80.jpg">
                                                            <media:credit><![CDATA[Future / Edited with Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[kitchen items on gradient background]]></media:description>                                                            <media:text><![CDATA[kitchen items on gradient background]]></media:text>
                                <media:title type="plain"><![CDATA[kitchen items on gradient background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/C6yXKeqm2Lhdhh9Qq68X8a-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I love my kitchen, but there are some days when I want to replace every single utensil I own. I'm talking mismatched cutlery and crockery, a hard-to-clean stainless steel pan, and some ageing kitchen scales that have served me and my baking skills well over the last few years. </p><p>Upgrades like these are ones I've been putting off, simply because I don't have the means to replace everything. And I'm sure most of us would agree that it's easier to shove an old can opener or chipped plate back into the kitchen drawer than try to find a replacement. </p><p>Thankfully, it's <a href="https://www.tomsguide.com/live/news/amazon-prime-day-2026-au" target="_blank">Prime Day</a>, and I've found 24 discounts on kitchen accessories, utensils and small appliances that will upgrade your kitchen for under AU$150 on Amazon. Granted, there's one item on this list — <a href="https://www.amazon.com.au/dp/B07QCD5QX2/ref=sspa_dk_detail_0" target="_blank">namely, the Corelle Portofino 18-piece dinner set</a> — that is a bit above this price, at AU$159.20; however, I've had my eye on it for a while, and it's finally within my reach at AU$40 off RRP. And for an 18-piece set, that's only AU$8.80 per piece, which is a big win in my books. </p><p>Another great deal I've spotted is on <a href="https://www.amazon.com.au/Shark-Cordfree-Handheld-WV203-Graphite/dp/B07X82QCY7/" target="_blank">Shark's Cordfree Handheld vacuum, now AU$135.85</a>. While it may be a bit odd to put on a kitchen deals round up, I swear by my handheld vacuum for all the teeny crumbs and coffee grinds in my kitchen. Trust me, it is <em>life-changing</em>. </p><p>Don't just take my word for it, though — all these products come with 4+ star ratings, including those of my colleagues at Tom's Guide. Better yet, these deals are exclusive to Prime members, and if you're not signed up already, you can get a <a href="https://www.amazon.com.au/tryprimefree" target="_blank">free 30-day trial here</a>. You'll need to act fast to nab any of these kitchen deals, though, as they will expire on <strong>Monday, July 13, at 11:59pm. </strong></p>        <div class="featured_product_block featured_block_hero" data-id="ee965c88-7c08-11f1-a318-d1b673bf5819">            <a href="https://www.amazon.com.au/Wireless-Thermometer-INT-11I-B-Bluetooth-Waterproof/dp/B0F3XHR5Q6" data-model-name="Mini Wireless Meat Thermometer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/GDpGqAWeoHB669CnbKsU7g.jpg" alt="Inkbird Mini Wireless Meat Thermometer Int-11i-B Bluetooth 5.4 Bbq Food Probe Ip67 Waterproof Led Display for Bbq Grill Oven Smoker Outdoor Camping Kitchen"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Inkbird</div>                                        <div class="featured__title">Mini Wireless Meat Thermometer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965cf6-7c08-11f1-b53b-6937dc3e2575">            <a href="https://www.amazon.com.au/BRITA-Filter-PRO-Performance-Cartridge/dp/B0D7M7ZCJX" data-model-name="Flow Cask Water Filter Dispenser" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/yKfpnyJveQosy5QQgiKUsa.jpg" alt="Brita Flow Cask Water Filter Dispenser, Blue, 8.2l Capacity, Bpa-Free With Tap and 1 Maxtra Pro Cartridge"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>BRITA</div>                                        <div class="featured__title">Flow Cask Water Filter Dispenser</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965d64-7c08-11f1-9619-7548055619ba">            <a href="https://www.amazon.com.au/Automatic-Preservation-Cleaning-Stainless-Indicator/dp/B08LCXZYLK" data-model-name="Vacuum Sealer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:85.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/b9LpQumfcZSCNmy4Hrs7xa.jpg" alt="Inkbird Vacuum Sealer Automatic Sealing Machine for Food Preservation Dry Moist Sealing Modes With Built-In Cutter Starter Kit Easy Cleaning Stainless Steel Panel Compact Design Led Indicator Lights"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Inkbird</div>                                        <div class="featured__title">Vacuum Sealer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965dbe-7c08-11f1-a070-61669ed4fc92">            <a href="https://www.amazon.com.au/AmazonBasics-Stainless-Digital-Batteries-Included/dp/B06X9NQ8GX/" data-model-name="Digital Kitchen Scale With LCD Display" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jD5qV6iEvWHnumfLsaTnFg.jpg" alt="Amazon Basics Digital Kitchen Scale With Lcd Display, Batteries Included, Weighs Up to 4.9 Kgs, Black and Stainless Steel"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon Basics</div>                                        <div class="featured__title">Digital Kitchen Scale With LCD Display</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_standard" data-id="ee965e22-7c08-11f1-bc5a-5daa4a14db74">            <a href="https://www.amazon.com.au/Shark-Cordfree-Handheld-WV203-Graphite/dp/B07X82QCY7/" data-model-name="Cordfree Handheld Vacuum" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/xdPTMjpXZRrHy3vujHFYKW.jpg" alt="Shark Cordfree Handheld Vacuum, Ultra-Lightweight and Portable With Powerful Suction & Tools for Pets, Designed for Car and Home, Grey, Wv203"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shark</div>                                        <div class="featured__title">Cordfree Handheld Vacuum</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_standard" data-id="ee965e86-7c08-11f1-8825-6746df393c77">            <a href="https://www.amazon.com.au/CIRCLE-JOY-Rechargeable-Cappuccino-Chocolate/dp/B0DPG6SNV1/" data-model-name="Double Whisk Milk Frother" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/JWecjk8LDzWwGR8MjkDYNW.jpg" alt="Circle Joy Powerful Double Whisk Milk Frother Handheld Rechargeable Milk Foamer Coffee Frother Drink Mixer With Cover, Portable for Coffee, Cappuccino, Lattes, Frappe, Matcha and Hot Chocolate, Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>CIRCLE JOY</div>                                        <div class="featured__title">Double Whisk Milk Frother</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965eea-7c08-11f1-b50a-df232d73edc1">            <a href="https://www.amazon.com.au/Corelle-18-Piece-Dinnerware-Resistant-Lightweight/dp/B0119IOPIY" data-model-name="Dinnerware Set, Country Cottage (18-Piece Set)" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/sa8hsWZmrjDRGWinKRyyAb.jpg" alt="Corelle Dinnerware Set, Country Cottage (18-Piece Set)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>CORELLE</div>                                        <div class="featured__title">Dinnerware Set, Country Cottage (18-Piece Set)</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965f44-7c08-11f1-bc9d-3f2737d0db5b">            <a href="https://www.amazon.com.au/AmazonBasics-Enameled-Cast-Iron-Dutch/dp/B073Q9K2H3" data-model-name="Enamelled Cast Iron Covered Round Dutch Oven" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/f2rA3bavQLLoYE2s6aUCQb.jpg" alt="Amazon Basics Enameled Cast Iron Covered Round Dutch Oven, 5.7 Liters / 6 Quart, Red"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon Basics</div>                                        <div class="featured__title">Enamelled Cast Iron Covered Round Dutch Oven</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee965fa8-7c08-11f1-b660-97e8fd267227">            <a href="https://www.amazon.com.au/Pyrex-Fryer-Glass-Baking-Piece/dp/B0DK38BPP7/" data-model-name="Air Fryer Glass Baking Cups 4 Piece Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/WPrByfmBrYZ52YYx7LaD2b.jpg" alt="Pyrex Air Fryer Glass Baking Cups 4 Piece Set – Borosilicate Glass Bakeware and Silicone Grip Pad – Microwave, Oven, Air Fryer, Freezer & Dishwasher Safe"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Pyrex</div>                                        <div class="featured__title">Air Fryer Glass Baking Cups 4 Piece Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966002-7c08-11f1-8d1b-f727d3c6c704">            <a href="https://www.amazon.com.au/Tefal-Non-Stick-C2890402-Aluminium-Technology/dp/B0CDQFTXZT" data-model-name="Daily Expert Red Non-Stick Frypan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/KqSK9KsxMBGJpwKc5h2iJb.jpg" alt="Tefal Daily Expert Red Non-Stick Frypan, 24cm, C2890402, Fixed Handle Aluminium, Titanium Non-Stick Coating, Thermo Signal Technology for Easier Cooking, Suitable for All Cooktops, Oven Safe"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Tefal</div>                                        <div class="featured__title">Daily Expert Red Non-Stick Frypan</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee96605c-7c08-11f1-8914-1921d3c7160e">            <a href="https://www.amazon.com.au/dp/B07QCD5QX2/ref=sspa_dk_detail_0" data-model-name="Portofino 18 Piece Dinnerware Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/zui8FFA2tqr9EogiBbd57b.jpg" alt="Corelle Portofino 18 Piece Dinnerware Set – Lightweight and Chip-Resistant Glass – Microwave and Dishwasher Safe – 6 X 26cm Dinner Plate, 6 X 17cm Side Plate, 4 X 532ml Soup/cereal Bowl"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>CORELLE</div>                                        <div class="featured__title">Portofino 18 Piece Dinnerware Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee9660c0-7c08-11f1-a70c-03556db61140">            <a href="https://www.amazon.com.au/Scanpan-Impact-Saucepan-Quality-Stainless/dp/B075LDQZMQ/" data-model-name="Impact 3-Piece Stainless Steel Saucepan Set With Tempered Glass Lids" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:93.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/RvFDeAUyTYH6Jcqc9UzH4g.jpg" alt="Scanpan Impact 3-Piece Stainless Steel Saucepan Set With Tempered Glass Lids,silver"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Scanpan</div>                                        <div class="featured__title">Impact 3-Piece Stainless Steel Saucepan Set With Tempered Glass Lids</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee96611a-7c08-11f1-8c4d-7be995c89c14">            <a href="https://www.amazon.com.au/Tefal-Non-Stick-Indicator-Recycled-G2933202/dp/B0D87HTGR6" data-model-name="Delight Sauté Pan, non stick" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:62.80%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/hxzNAHZaVGPyD47nrnPhmf.jpg" alt="Tefal Delight Sauté Pan 24 Cm, Non-Stick Coating, Heat Indicator, 100% Recycled Aluminum Body, G2933202"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Tefal</div>                                        <div class="featured__title">Delight Sauté Pan, non stick</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966174-7c08-11f1-8ee0-559a039e5c40">            <a href="https://www.amazon.com.au/Joseph-4-piece-Chopping-Storage-Regular/dp/B07YGJMNVK" data-model-name="Folio 4-Piece Chopping Board Set With Storage Case" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:75.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/VBUaFWiKupvKdmyTh95oHb.jpg" alt="Joseph Joseph Folio 4-Piece Chopping Board Set With Storage Case (regular) - Graphite"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Folio 4-Piece Chopping Board Set With Storage Case</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee9661d8-7c08-11f1-a85c-0b6a48335e4e">            <a href="https://www.amazon.com.au/GreenLife-Artisan-Healthy-Ceramic-Nonstick/dp/B098FGB5XC" data-model-name="Artisan Healthy Ceramic Nonstick Frying Pan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ddGTw2viSjbAKQsKj6BwLb.jpg" alt="Greenlife Artisan Healthy Ceramic Nonstick, Frying Pan/skillet Set, 8" and 10", Yellow"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>GreenLife</div>                                        <div class="featured__title">Artisan Healthy Ceramic Nonstick Frying Pan</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966232-7c08-11f1-ae8f-4d12651e9b9b">            <a href="https://www.amazon.com.au/Pyrex-Deep-Baking-6-Piece-BPA-Free/dp/B07K14QNCC" data-model-name="Deep Dish Glass Baking Dishes With Sage Lids" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/tqCYcGNY6qZ4ahtWMNbVjf.jpg" alt="Pyrex 1135014 Deep Dish Glass Baking Dishes With Sage Lids – Extra Deep Design and Durable Tempered Glass – Dishwasher, Microwave, Freezer Safe – 6 Piece Set"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Pyrex</div>                                        <div class="featured__title">Deep Dish Glass Baking Dishes With Sage Lids</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966296-7c08-11f1-b321-ff9e9676d1b8">            <a href="https://www.amazon.com.au/Stainless-Steel-Griddle-Scraper-Chopper/dp/B072MTB9H8/" data-model-name="Stainless Steel Metal Griddle Scraper" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rzBsWFaevc6g3xWBEGPEgL.jpg" alt="Stainless Steel Metal Griddle Scraper Chopper - Great as Dough Cutter for Bread and Pizza Dough - Multipurpose Kitchen Utensil for Flat Top Griddle - Commercial Quality"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Anmarko</div>                                        <div class="featured__title">Stainless Steel Metal Griddle Scraper</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee9662f0-7c08-11f1-a6b4-b7367ced3f80">            <a href="https://www.amazon.com.au/Joseph-Editions-9-piece-compact-preparation/dp/B07TT79CYC/" data-model-name="Editions Nest 9 Plus Preparation set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/pfubmd2GqJaXFrTyLnndcL.jpg" alt="Joseph Joseph Editions Nest 9 Plus, 9-Piece Compact Food Preparation Set - Sky"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Editions Nest 9 Plus Preparation set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966354-7c08-11f1-89cd-7fb9b45d7ac7">            <a href="https://www.amazon.com.au/Wiltshire-Bakeware-Smart-Stack-Rose/dp/B0B65T2D6Z/" data-model-name=" 7 Piece Smart Stack Bakeware Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:140.45%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ez74US7CH2egDafsuQMvkL.jpg" alt="Wiltshire 7 Piece Smart Stack Bakeware Set Rose Gold – Non-Stick Baking Trays, Cake Tins, Muffin Pan and Roasting Pan. Space-Saving Storage Solution in Gift Box"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Wiltshire</div>                                        <div class="featured__title"> 7 Piece Smart Stack Bakeware Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee9663ae-7c08-11f1-be36-63127958b76e">            <a href="https://www.amazon.com.au/Joseph-Scoop-Plus-Colander-Large/dp/B07FMB4RN8/" data-model-name="Scoop Plus " data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:54.40%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/FyL8FBNheXdotvZWKHhdaL.jpg" alt="Joseph Joseph Scoop Plus Large - Grey/green"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Scoop Plus </div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966408-7c08-11f1-80c7-211da0ab45c4">            <a href="https://www.amazon.com.au/JOSEPH-20101-Juicer-Citrus-Yellow/dp/B01N2Y8QK1" data-model-name="Helix Citrus Press" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:62.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/N47YaJSC3xzpHQpWCL6iWL.jpg" alt="Joseph Joseph Helix Citrus Press,yellow and Green"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Joseph Joseph</div>                                        <div class="featured__title">Helix Citrus Press</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee96646c-7c08-11f1-bc10-f7ecfcfea2b3">            <a href="https://www.amazon.com.au/D%C3%A9cor-Cook-Refillable-Oil-Sprayer/dp/B07C5S3LHD" data-model-name="Refillable Oil Sprayer" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:150%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/qWbtL8UkZj69VUjAo2Lzen.jpg" alt="Décor Décor Cook Refillable Oil Sprayer,white"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Décor</div>                                        <div class="featured__title">Refillable Oil Sprayer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee9664c6-7c08-11f1-92c1-63f875c81c00">            <a href="https://www.amazon.com.au/Magnetic-Measuring-Stainless-Stackable-Ingredients/dp/B09W9Z52B2" data-model-name="Magnetic Measuring Spoons Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/hgJEsk8hgJXxSiBhHZTGiL.jpg" alt="Magnetic Measuring Spoons Set, 7 Measuring Spoons and Cups 1 Leveler Stainless Steel Dual Sided Stackable Teaspoon for Liquid and Dry Ingredients"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Hushnow</div>                                        <div class="featured__title">Magnetic Measuring Spoons Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ee966520-7c08-11f1-bea9-6b7dff875bd3">            <a href="https://www.amazon.com.au/Amazon-Basics-Baking-Bread-24-13/dp/B073P52PPR" data-model-name="Baking Bread Loaf Pan, 2pk" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:57.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/QhRNuojdFRcW24A9Bxknid.jpg" alt="Amazon Basics Baking Bread Loaf Pan, 24.1 X 12.7 Cm, Set of 2"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon Basics</div>                                        <div class="featured__title">Baking Bread Loaf Pan, 2pk</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
            </channel>
</rss>