<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="https://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/homes" rel="self" type="application/rss+xml" />
                            <title><![CDATA[ Latest from Tom's Guide in Homes ]]></title>
                <link>https://www.tomsguide.com/tag/homes</link>
        <description><![CDATA[ All the latest homes content from the Tom's Guide team ]]></description>
                                    <lastBuildDate>Fri, 31 Jul 2026 09:45:00 +0000</lastBuildDate>
                            <language>en</language>
                                <item>
                                                            <title><![CDATA[ I put a battery-powered induction stove to the test — here’s what surprised me ]]></title>
                                                                                                                                                                                                <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 &amp; 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[ The US government just banned almost all robot vacuums — here's why (Update: companies respond) ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/smart-home/the-us-government-just-banned-almost-all-robot-vacuums-heres-why</link>
                                                                            <description>
                            <![CDATA[ The US government has announced a ban on "advanced robotic devices" entering the US. That means robot vacuums, robot lawnmowers and delivery robots on the ground are all under threat. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">yGyrPpxCVTbMkGtBiieWxT</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/WWbMLEt9LxVc2AR2drEipF-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 30 Jul 2026 11:24:15 +0000</pubDate>                                                                                                                                <updated>Fri, 31 Jul 2026 19:49:34 +0000</updated>
                                                                                                                                            <category><![CDATA[Smart Home]]></category>
                                                    <category><![CDATA[Home]]></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/WWbMLEt9LxVc2AR2drEipF-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[iRobot Roomba i3+ review]]></media:description>                                                            <media:text><![CDATA[iRobot Roomba i3+ review]]></media:text>
                                <media:title type="plain"><![CDATA[iRobot Roomba i3+ review]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/WWbMLEt9LxVc2AR2drEipF-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you've been watching the news, you may have seen the Trump administration's recent robotic ban. In a move by the Federal Communications Commission (FCC), the ban targets any new foreign-made <em>humanoid</em> robot imports to the U.S. The thinking is that these products may cause "unacceptable risks" to U.S. security.</p><p>But humanoid robots <strong>aren't the only robots at risk</strong>. It turns out the ban spreads much further than you might think. Far enough to spell trouble for the kind of 'bots you may be using in your own home.</p><p>Delve a little deeper into the <a href="https://www.fcc.gov/document/fcc-adds-foreign-produced-power-inverters-and-robots-covered-list-0" target="_blank">FCC's definition,</a> and you're looking at a <strong>much larger coverage of robots that fall under the term "advanced robotic devices"</strong>.  </p><p>If they're <strong>"capable of locomotion, obstacle avoidance, navigation, or movement on the ground"</strong> then they're targeted under the new ban. Sounds a lot like robotic lawnmowers, the <a href="https://www.tomsguide.com/us/best-robot-vacuums,review-2000.html">best robot vacuums</a> or even your friendly neighborhood delivery robot, right? Exactly.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-exVMmO"></div>                            </div>                            <script src="https://kwizly.com/embed/exVMmO.js" async></script><h2 id="no-more-roombas">No more Roombas?</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:1393px;"><p class="vanilla-image-block" style="padding-top:56.07%;"><img id="pAsxEDhyFTzycwLvC2whue" name="Roomba S9+" alt="Roomba S9+" src="https://cdn.mos.cms.futurecdn.net/pAsxEDhyFTzycwLvC2whue.jpg" mos="" align="middle" fullscreen="" width="1393" height="781" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: iRobot)</span></figcaption></figure><div><blockquote><p>Any new 'advanced robotic devices' being imported are banned, which essentially means any robots manufactured outside of the U.S.</p></blockquote></div><p>Now, it's worth delving into exactly what this means for home and delivery robots that fall under the FCC's definition. </p><p>Importantly, any existing robots previously authorised to be sold in the U.S. are safe. Equally, any robots that have already been imported from outside of the U.S., are currently being sold, or have been approved are also safe.</p><p>But any new "advanced robotic devices" being imported, which essentially means any robots being manufactured outside of the U.S., are banned.</p><p>This includes robots where "the combined weight of the device and, if applicable, ground station or docking station is over 4.4 lbs", as well as any with "software running either locally or remotely, including firmware and AI or machine-learning model weights" and "a sensor capable of perceiving its environment" or "a component that is capable of providing network connectivity (wired or wireless, including Bluetooth/Wi-Fi, cellular, or satellite)".</p><p>Why? Well, as the <a href="https://docs.fcc.gov/public/attachments/DA-26-786A1.pdf" target="_blank">FCC</a> has said, they consider these robots to pose a national security risk, stating: "foreign-produced advanced robotic devices could be weaponized to directly harm U.S. national security and U.S. persons". </p><p>But what's <em>really</em> interesting is what it's demanding companies do instead.</p><h2 id="safety-concerns">Safety concerns?</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:11396px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rUzUSbtwusZspBvdY3vZn7" name="Roborock-Hero" alt="The Roborock Saros Z70 picking up a shoe" src="https://cdn.mos.cms.futurecdn.net/rUzUSbtwusZspBvdY3vZn7.jpg" mos="" align="middle" fullscreen="" width="11396" height="6410" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Roborock)</span></figcaption></figure><p>A lot of companies like Roomba, Ecovacs, Dreame, Roborock and more source manufacturing from China. Moving forward, they'll need to acquire waivers to continue to manufacture and sell these advanced robotic devices in the US. But, <a href="https://www.theverge.com/policy/972312/us-robot-ban-sweep-up-chinese-vacuums" target="_blank">The Verge</a> revealed the FCC doesn't want to ask any questions about actual security.</p><p>Instead, the site reports: "The FCC only wants to know <em>where </em>they're designed and made and assembled and tested and influenced, and get a specific commitment to start manufacturing them in the U.S. instead."</p><p>So, for now, existing robots in the U.S. are safe. You won't have to hand over your much-loved robot vacuum to security. But it's a <em>huge</em> deal for the companies making them. Any new models will need a waiver to be manufactured elsewhere, and if they can't get the waiver, then they won't be getting sold in the U.S. </p><p>If you were on the fence about picking up one of the <a href="https://www.tomsguide.com/us/best-robot-vacuums,review-2000.html">best robot vacuums</a> or a robotic lawnmower, you might want to act sooner rather than later.</p><div class="product"><a data-dimension112="1f140922-8c09-11f1-a386-33dcaf7c427a" data-action="Deal Block" data-label="The Q7 L5 tackles everyday cleaning with ease, pulling out dust deep from carpets. Plus, its rugged wheels and bouncy suspension mean it's great at climbing over cables and handling uneven floors." data-dimension48="The Q7 L5 tackles everyday cleaning with ease, pulling out dust deep from carpets. Plus, its rugged wheels and bouncy suspension mean it's great at climbing over cables and handling uneven floors." data-dimension25="$139" href="https://www.amazon.com/roborock-Q7-Anti-Tangle-Multi-Level-Self-Charging/dp/B0F334DN9J" 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="nTwuXCqZHGt7dBQq8jHrt4" name="Roborock Q7 M5 robot vacuum" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/nTwuXCqZHGt7dBQq8jHrt4.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Q7 L5 tackles everyday cleaning with ease, pulling out dust deep from carpets. Plus, its rugged wheels and bouncy suspension mean it's great at climbing over cables and handling uneven floors.<a class="view-deal button" href="https://www.amazon.com/roborock-Q7-Anti-Tangle-Multi-Level-Self-Charging/dp/B0F334DN9J" target="_blank" rel="nofollow" data-dimension112="1f140922-8c09-11f1-a386-33dcaf7c427a" data-action="Deal Block" data-label="The Q7 L5 tackles everyday cleaning with ease, pulling out dust deep from carpets. Plus, its rugged wheels and bouncy suspension mean it's great at climbing over cables and handling uneven floors." data-dimension48="The Q7 L5 tackles everyday cleaning with ease, pulling out dust deep from carpets. Plus, its rugged wheels and bouncy suspension mean it's great at climbing over cables and handling uneven floors." data-dimension25="$139">View Deal</a></p></div><h2 id="cta-responds">CTA responds</h2><p>The Consumer Technology Association, the organization behind CES, also issued a measured response on X in a series of posts. </p><div class="see-more see-more--clipped"><blockquote class="twitter-tweet hawk-ignore" data-lang="en"><p lang="en" dir="ltr">At CES 2026, robotics took center stage. Innovation in this space is accelerating fast, and the U.S. must lead the world in developing and deploying these technologies. 🧵<a href="https://twitter.com/cantworkitout/status/2082853441462841735">July 30, 2026</a></p></blockquote><div class="see-more__filter"></div></div><p>"At CES 2026, robotics took center stage. Innovation in this space is accelerating fast, and the U.S. must lead the world in developing and deploying these technologies."</p><p>"CTA supports efforts to protect our national security and strengthen technology supply chains. But those policies should be targeted, transparent and focused on genuine security risks, not unnecessary costs or supply chain disruption."</p><p>"The global race in robotics is on. CTA looks forward to working with the Administration to ensure America leads the world in robotics and the next generation of emerging technologies."</p><p>"We encourage continued engagement between industry and government to ensure policies advance trusted supply chains while preserving innovation, competition and consumer access to new technologies."</p><h2 id="companies-respond">Companies respond</h2><p>We reached out to a number of companies following the FCC's announcement. Here's what they had to say. </p><p><strong>Anker Innovations</strong></p><p>"We are aware of the FCC’s recent update. Based on our current understanding, the new rule does not affect the continued availability, use or support of products we currently sell in the United States. Those products remain available and will continue to receive updates and customer support.</p><p>"Like others across the industry, we are seeking further clarity on how the rule will be applied to future products, and we will keep our customers informed. We remain committed to complying with all regulatory requirements in every market where we operate."</p><p><strong>Dreame</strong></p><p>"We are currently reviewing and assessing the details of the recent announcement," a spokesperson said. "Dreame has always been committed to strict compliance with all local laws and regulatory requirements in every market where we operate globally."</p><p><strong>iRobot (makers of Roomba)</strong></p><p>"We are aware of the FCC's recent action and are working with them to better understand its implementation and potential impact. We remain committed to serving our customers and will share updates as more information becomes available."</p><p><strong>Segway Navimow</strong></p><p>"We are aware of the FCC’s announcement regarding restrictions to advanced robotic devices. There is no immediate impact to existing Navimow customers, as all Navimow models currently sold in the US have already received FCC equipment authorization. The ban does not affect the sales, service, or continued use of existing products. We are closely monitoring policy developments and evaluating compliance pathways for future products, including the Department of Defense's "Conditional Approval" process."</p><p>What do you think of the ban? Let us know in the comments.</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/home-appliances/dji-romo-p-the-one-robot-vacuum-i-wish-was-coming-to-the-states-but-what-to-buy-instead">Here’s the one robot vacuum I wish was coming to the US (and what to buy instead)</a></li><li><a href="https://www.tomsguide.com/best-picks/best-cheap-robot-vacuums">The 4 best cheap robot vacuums you can buy in 2026</a></li><li><a href="https://www.tomsguide.com/home/im-a-robot-vacuum-expert-and-after-seeing-this-vacuum-that-can-climb-over-obstacles-its-the-only-feature-i-care-about">I’m a robot vacuum expert, and after seeing this vacuum that can climb over obstacles, it’s the only feature I care about</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Furnishing a bedroom? I did the math on prices at IKEA, Lowe's and Home Depot to find out which is the winner — the result may surprise you ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/furnishing-a-bedroom-i-did-the-math-on-prices-at-ikea-lowes-and-home-depot-to-find-out-which-is-the-winner-the-result-may-surprise-you</link>
                                                                            <description>
                            <![CDATA[ Whenever I do a refurb, I turn to IKEA for its budget-friendly and easy-to-buy options. But, what if I shopped around at Lowe's and Home Depot? I put it to the test on a bedroom redesign. Here's what happened. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">eqeX4VbCinBEmE8m8mLhx4</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/ra6FuG3hhbUqMCvjkBD444-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Thu, 30 Jul 2026 05:45:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></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/png" url="https://cdn.mos.cms.futurecdn.net/ra6FuG3hhbUqMCvjkBD444-1280-80.png">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Retail stores lead image]]></media:description>                                                            <media:text><![CDATA[Retail stores lead image]]></media:text>
                                <media:title type="plain"><![CDATA[Retail stores lead image]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/ra6FuG3hhbUqMCvjkBD444-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Whether I'm planning a redesign or I've got a new room that needs fully furnishing, I always tend to shop in the same place – IKEA. </p><p>The blue and yellow allure and modular designs have continuously and cleverly been sold to me as the most budget-friendly option for an entire collection of furniture. But, is that <em>actually</em> the case?</p><p>Don't get me wrong, I know there's other options. In fact, the market has largely been dominated for decades by two juggernaut home retailers – Lowe's and Home Depot. </p><p>How do they hold up against IKEA though? I sometimes struggle to find clarity in which is the more affordable choice. </p><p>So, I thought I'd put it to the ultimate test. I mocked up a complete bedroom design and found similar items across IKEA, Lowes and Home Depot to compare the prices. </p><p><em>Spoiler alert</em>: you might be shocked by the results.</p><h3 class="article-body__section" id="section-different-stores-different-lures"><span>Different stores, different lures</span></h3><h2 id="pros-and-cons-of-ikea">Pros and cons of IKEA</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="qg5Z6rnAaTsHSyK9TWXmTN" name="IKEA-LEDE-1" alt="An closeup exterior shot of an IKEA store." src="https://cdn.mos.cms.futurecdn.net/qg5Z6rnAaTsHSyK9TWXmTN.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>Pros:</p><ul><li>Competitively priced</li><li>Unique, space-friendly designs</li><li>Lots of stock</li><li>Ease of collection</li></ul><p>Cons:</p><ul><li>Build-it-yourself flat packs</li><li>Potentially expensive shipping costs</li><li>Less than 100 stores in the US</li></ul><h2 id="pros-and-cons-of-lowe-s">Pros and cons of Lowe's</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="S8S9NkUQkYzUcgFJrwWqZJ" name="Lowes Black Friday deals.jpg" alt="Lowe's Black Friday deals" src="https://cdn.mos.cms.futurecdn.net/S8S9NkUQkYzUcgFJrwWqZJ.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>Pros:</p><ul><li>Price match guarantee</li><li>Stock diverse and exclusive range of brands</li><li>Over 1700 stores across US</li></ul><p>Cons:</p><ul><li>Less stock</li><li>Strict discounts</li><li>Less choice</li></ul><h2 id="pros-and-cons-of-home-depot">Pros and cons of Home Depot</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="6Lg2GZBcp9nKsL2dVWWfyY" name="Home Depot sales.jpg" alt="Home Depot Father's Day sales" src="https://cdn.mos.cms.futurecdn.net/6Lg2GZBcp9nKsL2dVWWfyY.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>Pros:</p><ul><li>Extensive range of brands</li><li>Lots of stock</li><li>Over 2000 stores in the US</li></ul><p>Cons:</p><ul><li>Concerns over delivery</li><li>Not as affordable</li><li>Warehouses overwhelmingly large</li></ul><h3 class="article-body__section" id="section-cost-comparison"><span>Cost comparison</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:3953px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="9cQxyWJdEMZynYWzhH8YrY" name="bedroom crop.jpg" alt="Bedroom" src="https://cdn.mos.cms.futurecdn.net/9cQxyWJdEMZynYWzhH8YrY.jpg" mos="" align="middle" fullscreen="" width="3953" height="2224" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Bedroom </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>To carry out this cost comparison, I set out to find items from each store that looked incredibly similar. Of course, if you're planning a different aesthetic, you can shop around and compare all three. </p><p>But to fully furnish a bedroom with a bed, wardrobe, dresser, and nightstand, here's what the costs had to say.</p><div ><table><caption>IKEA vs Lowes vs Home Depot bedroom decor price comparison</caption><tbody><tr><td class="firstcol " ><p><strong>Retailer</strong></p></td><td  ><p><strong>IKEA</strong></p></td><td  ><p><strong>Lowes</strong></p></td><td  ><p><strong>Home Depot</strong></p></td></tr><tr><td class="firstcol " ><p><strong>Bed</strong></p></td><td  ><p><a href="https://www.ikea.com/us/en/p/stjaernoe-bed-frame-anthracite-40570183/" target="_blank" rel="nofollow">STJÄRNÖ bed frame, Anthracite (Queen) </a></p><p>$229</p></td><td  ><p><a href="https://www.lowes.com/pd/Mondawe-Black-Queen-Metal-Bed-Frame/5015675731" target="_blank" rel="nofollow">Mondawe bed frame, Black metal (Queen)</a></p><p>$209</p></td><td  ><p><a href="https://www.homedepot.com/p/VECELO-Queen-Bed-Frame-with-Headboard-Heavy-Duty-Platform-Bed-Frame-No-Box-Spring-Needed-Under-Bed-Storage-Space-60-9-in-W-KHD-BTJ-MBQ08-BLK/321699841" target="_blank" rel="nofollow">VECELO bed frame, Black metal (Queen)</a></p><p>$232.20</p></td></tr><tr><td class="firstcol " ><p><strong>Wardrobe</strong></p></td><td  ><p><a href="https://www.ikea.com/us/en/p/brimnes-wardrobe-with-2-doors-white-30574799/" target="_blank" rel="nofollow">BRIMNES wardrobe, White</a></p><p>$199.99</p></td><td  ><p><a href="https://www.homedepot.com/p/FUFU-GAGA-White-Wood-31-5-in-Armoire-with-Metal-Hanging-Rod-and-6-Shelves-Modern-2-Door-Wardrobe-Closet-with-Storage-ZJ-JQ210085-01/340677403" target="_blank" rel="nofollow">FUFU&GAGA wardrobe, White</a></p><p>$217.41</p></td><td  ><p><a href="https://www.homedepot.com/p/FUFU-GAGA-White-2-Doors-Armoire-Wardrobe-with-Hanging-Rod-and-Storage-Shelves-71-in-H-x-31-5-in-W-x-15-7-in-D-WFKF210085-01-xin/319500117" target="_blank" rel="nofollow">FUFU&GAGA wardrobe, White</a></p><p>$235.10</p></td></tr><tr><td class="firstcol " ><p><strong>Dresser</strong></p></td><td  ><p><a href="https://www.ikea.com/us/en/p/storklinta-6-drawer-dresser-white-anchor-unlock-function-60561248/" target="_blank" rel="nofollow">STORKLINTA 6-drawer dresser, White</a></p><p>$249.99</p></td><td  ><p><a href="https://www.lowes.com/pd/FUFU-GAGA-6-drawer-dresser/5014112857" target="_blank" rel="nofollow">FUFU&GAGA 6-drawer dresser, White</a></p><p>$229</p></td><td  ><p><a href="https://www.homedepot.com/p/FUFU-GAGA-15-7-in-D-x-32-3-in-H-6-Drawers-White-Wood-59-in-W-Dresser-Organizer-KF200151-01-c/325597761" target="_blank" rel="nofollow">FUFU&GAGA 6-drawer dresser, White</a></p><p>$265.72</p></td></tr><tr><td class="firstcol " ><p><strong>Nightstand</strong></p></td><td  ><p><a href="https://www.ikea.com/us/en/p/hemnes-2-drawer-chest-white-stain-80242627/" target="_blank" rel="nofollow">HEMNES nightstand, White</a></p><p>$149.99</p></td><td  ><p><a href="https://www.lowes.com/pd/Signature-Design-by-Ashley-Fortman-White-Nightstand/7160884" target="_blank" rel="nofollow">Fortman nightstand, White</a></p><p>$209.99</p></td><td  ><p><a href="https://www.homedepot.com/p/Signature-DESIGN-BY-ASHLEY-Fortman-White-2-Drawer-25-in-W-Nightstands-B680-92/334069739" target="_blank" rel="nofollow">Fortman nightstand, White</a></p><p>$190.41</p></td></tr><tr><td class="firstcol " ><p><strong>Total cost</strong></p></td><td  ><p>$828.97</p></td><td  ><p><strong>$865.40</strong></p></td><td  ><p><strong>$923.43</strong></p></td></tr></tbody></table></div><p>It's also important to note that there can be additional costs when purchasing online, especially with delivery (varies dependent on location), but also including some potential extra costs for fittings, removal and storage fees.</p><h3 class="article-body__section" id="section-verdict"><span>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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6uG29Mi4idZCcpWqKP8EF7" name="IKEA store.shutterstock_1942005295 (1)" alt="IKEA storefront sign" src="https://cdn.mos.cms.futurecdn.net/6uG29Mi4idZCcpWqKP8EF7.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>So, it turns out while IKEA is still the most affordable during this price comparison, it's not by as far as I would have thought. </p><p>For Lowe's, it's less than a $40 difference. Home Depot is considered widely to be the more premium priced of the three, but still only came in around $60 more than Lowe's. </p><p>What was surprising about Home Depot though, was that three of the items chosen were the same product as Lowe's, yet more expensive. </p><p>I did also notice that some of Lowe's and Home Depot's prices changed a little during my comparison, whereas IKEA tends to stick to their prices (unless during a sales event).</p><p>Yes, IKEA is still the most affordable, but there are other things to consider, like delivery costs to you and accessibility to stores. It wasn't the total washout that I was expecting from IKEA, especially as far as Lowe's is concerned. </p><p>I know that when I make up a mood board for my next redesign, I'll be creating the look across all three retailers rather than assuming that I know the answer.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-O92wpX"></div>                            </div>                            <script src="https://kwizly.com/embed/O92wpX.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/home/ikeas-new-collection-is-designed-for-frequent-movers-im-excited-about-how-portable-and-affordable-it-is">IKEA's new collection is designed for frequent movers — I'm excited about how portable and affordable it is</a></li><li><a href="https://www.tomsguide.com/home/5-ways-to-update-your-bedroom-without-a-complete-revamp">5 ways to update your bedroom without a complete revamp</a></li><li><a href="https://www.tomsguide.com/home/how-to-create-a-show-home-aesthetic-7-interior-tips-to-try">How to create a show home aesthetic — 7 interior tips to try</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>
                                                                                                                                                                                                <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 &amp; 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="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." data-dimension48="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." 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="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." data-dimension48="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." 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="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." data-dimension48="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." 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>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="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." data-dimension48="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." 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'm married to a plumber — here are 3 costly mistakes you could be making right now ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/my-husbands-a-plumber-and-these-are-the-3-expert-approved-tips-he-says-every-homeowner-should-know</link>
                                                                            <description>
                            <![CDATA[ Having a plumber for a husband is super handy, and to share the wealth. I asked him for his top expert-approved tips to help avoid common household mistakes. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">5eZgfGpidynCjv68Hb4KQY</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/KRHRWF4jQrrQc2GWgtvNGj-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Tue, 28 Jul 2026 05:00:00 +0000</pubDate>                                                                                                                                <updated>Tue, 28 Jul 2026 11:11:09 +0000</updated>
                                                                                                                                            <category><![CDATA[Home]]></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/png" url="https://cdn.mos.cms.futurecdn.net/KRHRWF4jQrrQc2GWgtvNGj-1280-80.png">
                                                            <media:credit><![CDATA[Shutterstock and Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Plumber at work under a sink]]></media:description>                                                            <media:text><![CDATA[Plumber at work under a sink]]></media:text>
                                <media:title type="plain"><![CDATA[Plumber at work under a sink]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/KRHRWF4jQrrQc2GWgtvNGj-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Whenever I tell people my husband is a plumber, they usually say: "That must be so handy!" And I always agree, yes, it absolutely is. </p><p>Whenever there's a plumbing (or heating) problem in my house, he knows how to solve it, and we don't have to spend extra money doing it or calling an emergency plumber late at night. </p><p>But instead of gatekeeping his secrets, I thought I'd ask him to share three of his favorite expert-approved tips that he thinks will help anyone in their home, so you can avoid making common plumbing mistakes.</p><p>Trust me, I should know if they work, because I literally use all three of them whenever needed. Here's some expert advice (free of charge) that'll hopefully even save you money in the future. </p><p>Just be sure to stick around for all three; I've saved my favorite until last.</p><h2 id="1-don-t-pour-hot-oil-down-your-sink">1. Don't pour hot oil down your sink</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="P8jFdp6K88rhckcBhSQ7Ac" name="hot_oil_hack" alt="Foil in a bowl for hot oil disposal" src="https://cdn.mos.cms.futurecdn.net/P8jFdp6K88rhckcBhSQ7Ac.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Foil in a bowl for hot oil disposal </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Excess oil that you've been cooking with seems like a pretty reasonable thing to pour down your sink when you've finished with it. After all, it's greasy, runny, and edible. But <em>don't do it</em>. </p><p>As my plumber husband explains: "While hot oil is liquid when it's been heated up, once it cools down it sets hard. If you have an incorrect fall on your waste pipe, this oil can sit stationary inside it, leading to blockages. </p><p>"The best solution is to pour your hot oil into a foil-lined bowl, leave it to set, and then throw the foil directly into the trash once it has [congealed]."</p><h2 id="2-don-t-put-bleach-tablets-in-toilet-tanks">2. Don't put bleach tablets in toilet tanks</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="RuuL9W3k2nub5ZnWyrMH6c" name="cistern_hack_2" alt="Checking the tank on a toilet" src="https://cdn.mos.cms.futurecdn.net/RuuL9W3k2nub5ZnWyrMH6c.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="caption-text">Checking the cistern on a toilet </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Bleach tablets are my go-to for tackling limescale and germs in my toilets at home, but my husband always tells me that I shouldn't use them regularly and I certainly shouldn't be putting them into the tank of the toilet. </p><p>The thought hadn't crossed my mind, but he says it's quite a common mistake that people tend to make: "A bleach tablet has high concentrations of chlorine. This can cause internal parts of the [tank] to fail, leading to leaks and damage to your toilet's components."</p><p>So, while you think you may be cleaning your toilet thoroughly, you could actually be doing more harm than good, which can become quite a costly mistake. Just stick to popping them into the bowl.</p><div class="product"><a data-dimension112="69bd9a08-88ec-11f1-a376-1b13d887de96" data-action="Deal Block" data-label="In order for bleach cleaning tablets to work, they contain powerful bleaching ingredients which are great for tackling stubborn stains, but not great if you put them in your cistern. The sustained release will work wonders on your toilet, as long as you follow my husband plumber's helpful tip." data-dimension48="In order for bleach cleaning tablets to work, they contain powerful bleaching ingredients which are great for tackling stubborn stains, but not great if you put them in your cistern. The sustained release will work wonders on your toilet, as long as you follow my husband plumber's helpful tip." data-dimension25="$19.99" href="https://www.amazon.com/Vacplus-Toilet-Bowl-Cleaners-Automatical-Cleaning/dp/B09SZ7F5FY" 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="ukgP3AyXvqKNWwJYKs825E" name="bleach_tablets_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ukgP3AyXvqKNWwJYKs825E.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In order for bleach cleaning tablets to work, they contain powerful bleaching ingredients which are great for tackling stubborn stains, but not great if you put them in your cistern. The sustained release will work wonders on your toilet, as long as you follow my husband plumber's helpful tip.<a class="view-deal button" href="https://www.amazon.com/Vacplus-Toilet-Bowl-Cleaners-Automatical-Cleaning/dp/B09SZ7F5FY" target="_blank" rel="nofollow" data-dimension112="69bd9a08-88ec-11f1-a376-1b13d887de96" data-action="Deal Block" data-label="In order for bleach cleaning tablets to work, they contain powerful bleaching ingredients which are great for tackling stubborn stains, but not great if you put them in your cistern. The sustained release will work wonders on your toilet, as long as you follow my husband plumber's helpful tip." data-dimension48="In order for bleach cleaning tablets to work, they contain powerful bleaching ingredients which are great for tackling stubborn stains, but not great if you put them in your cistern. The sustained release will work wonders on your toilet, as long as you follow my husband plumber's helpful tip." data-dimension25="$19.99">View Deal</a></p></div><h2 id="3-draw-a-pencil-line-around-damp-spots">3. Draw a pencil line around damp spots</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="MNmqKrgC9eakdpxzvgrFAc" name="pencil_hack" alt="Drawing a pencil line around a damp patch on the ceiling" src="https://cdn.mos.cms.futurecdn.net/MNmqKrgC9eakdpxzvgrFAc.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="caption-text">Drawing a pencil line around a damp patch on the ceiling </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Now, this one's my favorite, and it's one of those hacks you wonder why you didn't think of it yourself. </p><p>Whenever you spot a damp patch or stain in your home, here's what my plumber husband recommends: "I advise customers that if they notice a mark on their ceiling (most commonly), they should trace the outline with a pencil."</p><p>Why? Well, he adds: "If you leave it for 12 hours and come back, you'll see if the leak has gone past the pencil line. If it has, then you know you have an active leak." </p><p>And since a pencil is harmless to your ceiling, you can leave it a little longer to be extra sure or rub it away when you're fully satisfied that the leak has stopped.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XponGO"></div>                            </div>                            <script src="https://kwizly.com/embed/XponGO.js" async></script><p>Do you have any tips for saving money when it comes to plumbing? Let us know in the comments.</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/this-tiktok-hack-claims-vinegar-will-clean-your-shower-head-i-asked-my-plumber-husband-if-it-actually-works">This TikTok hack claims vinegar will clean your shower head — I asked my plumber husband if it actually works</a></li><li><a href="https://www.tomsguide.com/home/kitchen-dining/people-are-using-dish-soap-to-break-down-drain-blockages-i-asked-a-plumber-if-its-a-good-idea">You asked: Can dish soap fix your clogged drain? A plumber explains the facts</a></li><li><a href="https://www.tomsguide.com/home/kitchen-dining/households-urged-not-to-pour-boiling-water-down-drains-this-winter-i-asked-a-plumber-what-to-do-instead">Households urged not to pour boiling water down drains this winter — I asked a plumber what to do instead</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ This ultimate garden hack reveals why you should be planting your tomatoes horizontally ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/this-ultimate-garden-hack-reveals-why-you-should-be-planting-your-tomatoes-horizontally</link>
                                                                            <description>
                            <![CDATA[ Growing tomato plants can be a tricky garden task. I should know, I've failed on numerous attempts. Now, I've found out I should be planting my tomato plants horizontally, here's why. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">unWs6N7aH3Ps65BsVhA2BX</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/UPyx4JVFD9VPZ6gEZRzfoe-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 24 Jul 2026 08:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></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/UPyx4JVFD9VPZ6gEZRzfoe-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Tomato plant]]></media:description>                                                            <media:text><![CDATA[Tomato plant]]></media:text>
                                <media:title type="plain"><![CDATA[Tomato plant]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/UPyx4JVFD9VPZ6gEZRzfoe-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I've always found growing tomatoes tricky. Every year, I make a new attempt, but the results are inconsistent. One year, I'll get a few worthy of a salad, the next it'll be a total fail. </p><p>And as I sat out in my yard recently, I stared at this year's yield – tomato plants that had stopped growing entirely. So, I did what I love to do and headed online to social media to see if I could find an answer.</p><p>Fortunately, tomato growing hacks are never hard to find, from <a href="https://www.tomsguide.com/home/gardening/i-tried-this-zero-waste-gardening-hack-to-help-my-tomato-plants-thrive-and-all-i-needed-was-a-common-kitchen-scrap">using this common kitchen scrap for zero-waste gardening</a> to <a href="https://www.tomsguide.com/home/gardening-pros-swear-by-this-ingenious-tomato-watering-trick-try-it-for-juicier-plumper-tomatoes">trying out an ingenious tomato-watering trick</a>, but this time I was looking for something that would switch up my tomato growing game altogether.</p><p>Then, I found a TikTok that told me I've actually been doing it all wrong. It turns out we should all be planting our tomato plants horizontally. It might sound crazy, but it's actually a stroke of genius. Here's how it works.</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/@creative_explained/video/7654664045248744718" data-video-id="7654664045248744718" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@creative_explained" href="https://www.tiktok.com/@creative_explained">@creative_explained</a>                            <p></p><a target="_blank" title="♬ original sound - creative explained" href="https://www.tiktok.com/music/original-sound-7654664106951052045">♬ original sound - creative explained</a></section>                    </blockquote></div>                <p>I'm a sucker for TikTok creator, <a href="https://www.tiktok.com/@creative_explained" target="_blank" rel="nofollow">@creative_explained</a>'s videos and I'm not the only one. With over 7.5m followers, he's known for consistently sharing quick home and garden hacks with an enthusiasm that's infectious. I should know; I've been using his <a href="https://www.tomsguide.com/home/gardening/i-tried-this-genius-money-saving-hack-to-give-my-plants-a-boost-all-you-need-is-a-box-of-matches">money-saving hack for giving my plants a boost</a> this year, and I've been shocked by the results.  </p><p>But it's this tomato planting hack that caught my attention this time. As he demonstrates, you take a tomato plant and strip all the leaves off the stalk, leaving just the top shoot with one leaf. </p><p>Then, you dig a trench in your garden bed. After exposing the roots of your tomato plant and breaking apart the soil at the base, lay the entire plant horizontally into the trench.</p><p>Bury the entire stalk with soil, leaving just the top shoot and leaf exposed. Raise just this part and hold it vertically with soil. </p><p>He explains: "What's going to happen now underneath the soil is that the tomato will grow roots all along its stem. </p><p>It'll start to grow new shoots. The stem pretty much transforms into an entire root system." Genius.</p><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="ybpqyfqtMJj82jKWwxVz94" name="tomato_plant_hack" alt="Soil for planting" src="https://cdn.mos.cms.futurecdn.net/ybpqyfqtMJj82jKWwxVz94.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>I fear I may have found this hack a little late for the tomato growing season, but with hot weather in my area, I figured I could still transplant outside with a totally new tomato plant to hand. </p><p>Planting it horizontally was simple enough, even if it did feel like a strange process. I've had success with @creative_explained's hacks before like <a href="https://www.tomsguide.com/home/gardening/this-cheap-gardening-hack-uses-a-common-household-ingredient-youre-probably-throwing-away">using potato peel water in my soil</a>, so I'm looking forward to seeing the results. In terms of process, it was easy to do, though I needed to find a space in my garden bed that was long enough to accommodate.</p><p>Fortunately, I recently dug up some old rhubarb that was suffering in the heat, so I had the space to try out this horizontal planting trick. Now, I just need to wait until fall to see the results, but it feels good to try out something entirely new for a change. Growing tomatoes is a tricky business, but this might well be the solution I've been desperately searching for.</p><div class="product"><a data-dimension112="a3df4f40-84c5-11f1-8c30-c777546c4971" data-action="Deal Block" data-label="For indoor and outdoor use, this potting and planting soil expands with water up to 4x and can make up to 3 gallons worth of soil. Kept compact in this small bag, it's easy to store and bring out when you've got a planting mission in mind." data-dimension48="For indoor and outdoor use, this potting and planting soil expands with water up to 4x and can make up to 3 gallons worth of soil. Kept compact in this small bag, it's easy to store and bring out when you've got a planting mission in mind." data-dimension25="$39.97" href="https://www.amazon.com/Compressed-Organic-Potting-Soil-Garden-Plants/dp/B07DF9LN96" 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="nw2fuEKy7r6FfmJQ7GNecm" name="potting_soil_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/nw2fuEKy7r6FfmJQ7GNecm.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>For indoor and outdoor use, this potting and planting soil expands with water up to 4x and can make up to 3 gallons worth of soil. Kept compact in this small bag, it's easy to store and bring out when you've got a planting mission in mind.<a class="view-deal button" href="https://www.amazon.com/Compressed-Organic-Potting-Soil-Garden-Plants/dp/B07DF9LN96" target="_blank" rel="nofollow" data-dimension112="a3df4f40-84c5-11f1-8c30-c777546c4971" data-action="Deal Block" data-label="For indoor and outdoor use, this potting and planting soil expands with water up to 4x and can make up to 3 gallons worth of soil. Kept compact in this small bag, it's easy to store and bring out when you've got a planting mission in mind." data-dimension48="For indoor and outdoor use, this potting and planting soil expands with water up to 4x and can make up to 3 gallons worth of soil. Kept compact in this small bag, it's easy to store and bring out when you've got a planting mission in mind." data-dimension25="$39.97">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XZQZ5O"></div>                            </div>                            <script src="https://kwizly.com/embed/XZQZ5O.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/home/gardening/this-cheap-garden-hack-will-help-your-roses-grow-and-all-you-need-is-a-few-old-tea-bags">This cheap garden hack will help your roses grow — and all you need is a few old tea bags</a></li><li><a href="https://www.tomsguide.com/home/stop-wasting-money-on-tomato-feed-heres-how-i-made-my-own-free-fertilizer">Stop wasting money on tomato feed — here’s how I made my own free fertilizer</a></li><li><a href="https://www.tomsguide.com/home/gardening/the-3-kitchen-scraps-i-never-throw-away-if-i-want-sweet-ripe-tomatoes">The 3 kitchen scraps I never throw away if I want sweet, ripe tomatoes</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Yellow Monstera leaves signal stress — here’s how to save your plant and bring back the green ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/yellow-monstera-leaves-signal-stress-heres-how-to-save-your-plant-and-bring-back-the-green</link>
                                                                            <description>
                            <![CDATA[ I'll help you decode what your Monstera is trying to tell you — plus the simple tweaks you need to bring back healthy, green growth. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">UHaZSfmbNxp6iyQ3g2JHsQ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/M85ZY6VENL6vnYuCFF3YHC-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 23 Jul 2026 08:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></category>
                                                                                                <author><![CDATA[ kaycee.hill@futurenet.com (Kaycee Hill) ]]></author>                    <dc:creator><![CDATA[ Kaycee Hill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xHn6RmpEqg87cvtLwrBu9G.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/M85ZY6VENL6vnYuCFF3YHC-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Yellowing potted monstera plants]]></media:description>                                                            <media:text><![CDATA[Yellowing potted monstera plants]]></media:text>
                                <media:title type="plain"><![CDATA[Yellowing potted monstera plants]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/M85ZY6VENL6vnYuCFF3YHC-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Monsteras are usually the drama-free roommates of the houseplant world, until the morning you walk past yours and spot an undeniably yellow leaf.</p><p>Trust me, I know that sudden flash of panic. But after years of keeping these tropical giants happy, I’ve learned a yellow leaf isn't an instant death sentence. It’s just your plant’s version of a check-engine light. </p><p>Whether you're dealing with a simple watering mistake or a tricky lighting spot, I’ll help you translate what your Monstera is begging for and get it thriving again.</p><section class="howto-block">                    <h3>1. Assess your watering habits </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/kJDpXGLQ9dA8bjps3rY9VR.jpg"                                        alt="Yellowing monstera leaf caused by overwatering"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/kJDpXGLQ9dA8bjps3rY9VR.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Overwatering is the single most common cause of yellowing Monstera leaves. Monsteras need oxygen at their roots to survive. When potting soil stays waterlogged, roots suffocate, develop root rot, and lose the ability to transport nutrients to the foliage.</p><p>If overwatering is the issue, lower leaves will turn a solid, pale yellow, often accompanied by dark brown soft spots or a wet, musty smell coming from the soil. </p><p><strong>Stick your finger two to three inches into the soil. If it feels soggy, stop watering immediately</strong>. Allow the top half of the soil mix to dry completely before watering again, and <strong>always ensure your pot has functional drainage holes paired with a well-draining soil mix</strong>.</p><p>Underwatering causes a different pattern altogether. Leaves turn yellow with dry, crispy brown edges, and the entire plant feels droopy or papery to the touch.</p><p><strong>If the soil is bone-dry and pulling away from the inside edges of the pot, bottom-water or thoroughly soak the root ball</strong> until water drains freely from the bottom holes.</p></p>                </section><section class="howto-block">                    <h3>2. Adjust your lighting conditions </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/hB455b3MG4oG4U7RAzBhBL.jpg"                                        alt="Sun burned monstera"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/hB455b3MG4oG4U7RAzBhBL.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Monstera plants require medium to bright, indirect sunlight to photosynthesize effectively. Without enough light, the plant cannot process moisture in the soil, leading to both energy starvation and lingering wet roots.</p><p>When light is too low, leaves gradually fade from dark green to a dull, muted yellow, and new leaves grow unusually small without fenestrations. </p><p><strong>Move your plant closer to an east- or south-facing window behind a sheer curtain, or install a full-spectrum LED grow light </strong>positioned a few feet away. </p><p>Even though they're tropical plants, they dislike direct sunlight. If you notice yellow patches with bleached or crispy brown centers, this is likely the culprit. <strong>Move your plant away from where it'll be scorched by harsh sun</strong>. </p></p>                </section><section class="howto-block">                    <h3>3. Check for houseplant pests</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/ACatoM5cZHUtgdSVxbwVzn.jpg"                                        alt="Monstera leaf infested with pests"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/ACatoM5cZHUtgdSVxbwVzn.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Systematic pest attacks drain vital sap from plant tissue, causing yellow stippling, discoloration, and eventual leaf drop. </p><p>Thrips are particularly common on Monsteras and leave behind tiny, slender dark insects, black specks of excrement, and silvery patches on the leaves. Spider mites and mealybugs also target Monsteras, leaving behind delicate webbing or white, cottony residues.</p><p>If you spot pests,<strong> isolate the plant</strong> from your other houseplants immediately. <strong>Rinse or wipe down the leaves thoroughly with a mild soap solution</strong>, then <strong>spray all foliage with neem oil or insecticidal soap once a week</strong> until the infestation is completely cleared.</p></p>                </section><section class="howto-block">                    <h3>4. Address nutrient deficiencies or natural aging</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/vc3hXWS8N7viB59aDnVxff.jpg"                                        alt="Monstera plant with old, yellowed leaves "                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/vc3hXWS8N7viB59aDnVxff.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>If older, bottom leaves show general pale yellowing while the leaf veins stay slightly green, your Monstera may be lacking key nutrients like nitrogen. </p><p>Monstera plants grow rapidly during spring and summer and exhaust soil nutrients over time. <strong>Feed your plant every two to four weeks during the growing season with a balanced liquid houseplant fertilizer diluted to half strength</strong>, cutting back completely during winter dormancy.</p><p>Keep in mind that not all yellowing is a crisis. If a single, lowermost small leaf turns yellow and drops every once in a while while the rest of the plant flourishes, this is simply natural leaf turnover as the plant directs its energy to new growth.</p></p>                </section><h3 class="article-body__section" id="section-frequently-asked-questions"><span>Frequently asked questions</span></h3><h2 id="should-i-cut-off-yellow-monstera-leaves">Should I cut off yellow monstera leaves?</h2><p>Yes, you should prune them. Once a Monstera leaf has turned <em>fully </em>yellow, it cannot regain its green color because the chlorophyll has broken down. Use sterilized shears to cut the yellow leaf stem near the base so the plant stops wasting valuable energy trying to sustain damaged tissue.</p><h2 id="how-long-does-it-take-for-a-monstera-to-recover">How long does it take for a monstera to recover?</h2><p>Once you fix the underlying issue, such as adjusting your watering schedule or moving the pot to better lighting, you should expect to see fresh, healthy green growth within two to four weeks during the growing season.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-egKKpe"></div>                            </div>                            <script src="https://kwizly.com/embed/egKKpe.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/home/monstera-leaves-curling-heres-what-your-plant-is-trying-to-tell-you">Monstera leaves curling? Here's what your plant is trying to tell you</a></li><li><a href="https://www.tomsguide.com/home/gardening/this-tiny-pest-causes-the-biggest-damage-to-your-plants-heres-how-to-stop-thrips">This tiny pest causes the biggest damage to your plants — here's how to stop thrips</a></li><li><a href="https://www.tomsguide.com/home/houseplants-taking-over-heres-how-to-repot-a-swiss-cheese-plant">Houseplants taking over? Here's how to repot a monstera </a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ My tomato plants were dropping flowers — then I tried this 2-second pollination hack ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/my-tomato-plants-were-dropping-flowers-then-i-tried-this-2-second-pollination-hack</link>
                                                                            <description>
                            <![CDATA[ Struggling with blossom drop? Discover how tomato tapping and buzz pollination release pollen to save your flowers and double your yield. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">sfeWvKtNuLdg2Q3uv5HCjY</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/C58ZgBNsZdSvqGpaiRduRc-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 22 Jul 2026 08:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                <author><![CDATA[ kaycee.hill@futurenet.com (Kaycee Hill) ]]></author>                    <dc:creator><![CDATA[ Kaycee Hill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xHn6RmpEqg87cvtLwrBu9G.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/C58ZgBNsZdSvqGpaiRduRc-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Flowering tomato plant]]></media:description>                                                            <media:text><![CDATA[Flowering tomato plant]]></media:text>
                                <media:title type="plain"><![CDATA[Flowering tomato plant]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/C58ZgBNsZdSvqGpaiRduRc-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Seeing vibrant yellow blossoms drop off your tomato plants without ever setting fruit is easily one of the most frustrating parts of gardening. You’re watering correctly, feeding them on schedule, and the plants look healthy, so why aren't you getting any actual tomatoes?</p><p>The issue usually isn't your soil or your fertilizer routine at all. It comes down to a simple, hidden bottleneck inside the flowers themselves that stops fruit from forming. Luckily, there’s a 2-second technique that fixes the problem almost instantly, and all it takes is a quick touch of your fingertips.</p><h2 id="why-tomato-tapping-actually-works">Why tomato tapping actually works </h2><p>In nature, tomatoes rely on bumblebees for buzz pollination, a process where bees vibrate their wings to release heavy, trapped pollen locked inside the flower. When pollinators are scarce or plants are grown in sheltered spots like greenhouses, those flowers drop off unfertilized. </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:4000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="b4JdzZtQUEcjJnKBUCv7Hi" name="Hand touching flowering tomato plant" alt="Hand touching flowering tomato plant" src="https://cdn.mos.cms.futurecdn.net/b4JdzZtQUEcjJnKBUCv7Hi.jpg" mos="" align="middle" fullscreen="" width="4000" height="2250" 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>Tapping your plants simply mimics a bee's wing vibrations to break that pollen free and force the plant to set fruit. Timing is everything:<strong> tap your plants mid-morning, between 10 a.m. and 2 p.m., </strong>when morning dew has dried and pollen is light and dusty. </p><p>The process takes seconds, whether you <strong>lightly flick the stem behind an open blossom </strong>with your index finger or<strong> give the plant's wooden stake or metal cage a firm tap </strong>to send vibrations down the vine. Repeat your chosen method every two to three days while plants are actively blooming. </p><h2 id="what-to-expect-and-how-to-prepare-for-a-bumper-crop">What to expect (and how to prepare for a bumper crop)</h2><p>Within two to three weeks of consistent tapping, instead of bare stems and fallen petals, you'll see full, symmetrical clusters of green fruit swelling all at once.</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="5vYQwM9DpQSq8XQtdFk8cK" name="green tomatoes.jpg" alt="green tomatoes on vine" src="https://cdn.mos.cms.futurecdn.net/5vYQwM9DpQSq8XQtdFk8cK.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: Kzenon/Shutterstock)</span></figcaption></figure><p>Because your vines will be carrying significantly more weight than usual, proactive crop management is crucial to keep the plant healthy. Be sure to upgrade your support early by securing main stems to stakes with soft garden twine before branches start to sag. </p><p>Heavy beefsteak or cherry clusters can easily snap under their own weight, so cradle large fruit trusses with extra ties fastened directly to the main support. And make sure your stakes or cages are driven deep into the soil so top-heavy plants don't tip over. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eMqq3e"></div>                            </div>                            <script src="https://kwizly.com/embed/eMqq3e.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/home/gardening/when-to-fertilize-your-tomatoes-and-how-to-get-a-bountiful-harvest">When to fertilize tomatoes: the 3-step schedule for a bountiful harvest</a></li><li><a href="https://www.tomsguide.com/home/gardening/why-are-my-tomato-plant-leaves-turning-yellow-5-steps-to-save-your-harvest">Why are my tomato plant leaves turning yellow? 5 steps to save your harvest</a></li><li><a href="https://www.tomsguide.com/home/outdoors/gardeners-swear-by-this-simple-trick-heres-how-to-save-split-tomatoes-fast">Gardeners swear by this simple tomato plant trick — here's how to save split tomatoes fast</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The 3 kitchen scraps I never throw away if I want sweet, ripe tomatoes ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/the-3-kitchen-scraps-i-never-throw-away-if-i-want-sweet-ripe-tomatoes</link>
                                                                            <description>
                            <![CDATA[ Boost your tomato harvest for free using eggshells, banana peels, and coffee grounds to prevent rot, accelerate ripening, and fuel healthier plant growth. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">KVMJ3tjmxujW8VW2DwGdQ3</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/35xUxxG3VM9b8kqFBBpiyL-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 20 Jul 2026 09:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                                    <dc:creator><![CDATA[ Alice Carroll ]]></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/35xUxxG3VM9b8kqFBBpiyL-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Tony Tim cherry tomatoes]]></media:description>                                                            <media:text><![CDATA[Tony Tim cherry tomatoes]]></media:text>
                                <media:title type="plain"><![CDATA[Tony Tim cherry tomatoes]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/35xUxxG3VM9b8kqFBBpiyL-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you’ve ever watched your tomato plants struggle to transition from green, stubborn globes to juicy, vibrant red fruits, you aren’t alone. Getting tomatoes to perfectly ripen requires the right balance of nutrients, soil structure, and plant vitality. </p><p>Thankfully, you don't need store-bought supplements to give them a boost. The secret to a bumper crop of sweet, perfectly ripe tomatoes is sitting right in your kitchen garbage.</p><p>By strategically using crushed eggshells, banana peels, and coffee grounds, you can deliver a targeted nutrient cocktail that fuels fruit development and speeds up the ripening process. </p><section class="howto-block">                    <h3>1. Crushed eggshell </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/JoKvFNmBXp6v35qAJzzFoa.jpg"                                        alt="Powdered eggshells "                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/JoKvFNmBXp6v35qAJzzFoa.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Eggshells are packed with calcium carbonate, which helps build strong plant cell walls. While garden lore claims throwing eggshells in the soil is a quick fix for blossom-end rot (those black, leathery patches on the fruit), that is actually a myth. </p><p>Eggshells take months or years to decompose into a form roots can absorb, and blossom-end rot is usually caused by inconsistent watering rather than a lack of soil calcium. Even so, eggshells are a fantastic long-term investment that slowly enriches your garden bed for future crops.</p><p>To use them, <strong>wash your leftover eggshells, let them dry completely, and blitz them in a blender</strong> into a fine powder to help them break down faster. You can also use a pestle and mortar for this. </p><p><strong>Dig this powder directly into the soil at the base of your plants or sprinkle a handful into the planting hole</strong>. </p><p>Because tomatoes need water to move nutrients, make sure to pair this with a consistent watering schedule so the plant can successfully transport the soil's existing calcium up to the ripening fruit.</p></p>                </section><section class="howto-block">                    <h3>2. Banana peels </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/jxrKqywY687JgZjEqtyWX8.jpg"                                        alt="Home made tomato feed"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/jxrKqywY687JgZjEqtyWX8.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Tom's Guide / Camilla Sharman)</div></figure>                    <p><p>If you think of banana peels as the stuff of slapstick comedy, you may be surprised to learn that the humble peel is packed with nutrients — namely potassium, calcium, magnesium, and sulfur. </p><p>While calcium prevents rot, potassium is the true MVP of fruit production. It facilitates photosynthesis, regulates water uptake, strengthens stems, and directly drives the chemical processes that cause tomatoes to swell, sweeten, and ripen.</p><p>Banana peels shouldn’t completely replace regular fertilizer, but rather act as a powerful booster alongside it.</p><p><strong>Soak a few banana peels in a jar of water for 24 to 48 hours</strong>. Then<strong> remove the peels (throw them in the compost), and pour the nutrient-infused "tea" directly onto the soil around your plants</strong>.</p></p>                </section><section class="howto-block">                    <h3>3. Coffee grounds </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/iXAEyVAQiAww4GCd9ESBbY.jpg"                                        alt="Sprinkling coffee grounds on plants"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/iXAEyVAQiAww4GCd9ESBbY.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Leftover coffee grounds are an excellent source of nitrogen, which fuels the lush, green foliage tomato plants need to convert sunlight into sugars. These sugars are what ultimately pump into the green fruits to make them sweet and red. </p><p>Coffee grounds also improve soil texture, boost water retention, and attract beneficial earthworms that naturally aerate the root zone.</p><p>Never dump thick layers of wet grounds directly onto the soil. Instead,<strong> mix your used coffee grounds with a bit of dry mulch or compost and spread a thin layer around the base of your plants,</strong> gently working it into the surface. </p><p>Similarly to the banana 'tea', you can also can steep the grounds in a watering can overnight to create a mild, hydrating nitrogen liquid for your next watering session.</p></p>                </section><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Ww1z5X"></div>                            </div>                            <script src="https://kwizly.com/embed/Ww1z5X.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/home/gardening/when-to-fertilize-your-tomatoes-and-how-to-get-a-bountiful-harvest">When to fertilize tomatoes: the 3-step schedule for a bountiful harvest</a></li><li><a href="https://www.tomsguide.com/home/gardening/why-are-my-tomato-plant-leaves-turning-yellow-5-steps-to-save-your-harvest">Why are my tomato plant leaves turning yellow? 5 steps to save your harvest</a></li><li><a href="https://www.tomsguide.com/home/outdoors/gardeners-swear-by-this-simple-trick-heres-how-to-save-split-tomatoes-fast">Gardeners swear by this simple tomato plant trick — here's how to save split tomatoes fast</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ 5 mistakes you're making that turn your yard into a mosquito breeding ground ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/outdoors/5-mistakes-youre-making-that-turn-your-garden-into-a-mosquito-breeding-ground</link>
                                                                            <description>
                            <![CDATA[ Stop inviting mosquitoes to your patio: The simple backyard changes you need to make. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">cqgpK7GTeURoed24v7hi6U</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/i8BzrEdiXESpJJJk4MuNZh-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 17 Jul 2026 05:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Outdoors]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                                                                <author><![CDATA[ kaycee.hill@futurenet.com (Kaycee Hill) ]]></author>                    <dc:creator><![CDATA[ Kaycee Hill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xHn6RmpEqg87cvtLwrBu9G.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/i8BzrEdiXESpJJJk4MuNZh-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Mosquito on hand]]></media:description>                                                            <media:text><![CDATA[Mosquito on hand]]></media:text>
                                <media:title type="plain"><![CDATA[Mosquito on hand]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/i8BzrEdiXESpJJJk4MuNZh-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>There is nothing better than a warm summer evening spent relaxing in the garden. You've got the patio lights on, a cold drink in hand, and the perfect setup to unwind... until the buzzing starts.</p><p>Within minutes, a relaxing night outdoors turns into a frantic battle against mosquitoes. But before you douse yourself in sticky chemical sprays, look up at your patio lights. You might inadvertently be throwing an open-house party for every biting bug in the neighborhood.</p><p>To find out why our backyards are suddenly crawling with pests, I spoke to Jon Saeed, managing director of outdoor lighting specialist <a href="https://www.lightinglegends.com/?utm_campaign=gs-2021-01-10&utm_source=google&utm_medium=smart_campaign&gad_source=1&gad_campaignid=18314457799&gbraid=0AAAAAB6YBcKmedxJKc78Rzi8K-FdQqKiJ&gclid=Cj0KCQjwguLSBhDLARIsAH-yPrGDEKOw0mtbMcG28JcWzlWo1cHynaXFese6iD5gpZsy2HTgfpp88MwaAqWTEALw_wcB">Lighting Legends</a>.</p><p>According to Saeed, the warm summer evenings we love also bring along more mosquitoes who thrive in humid environments. But the real culprit behind your itchy ankles might actually be your choice of lightbulb.</p><p>Fortunately, you don't have to sit in the pitch black. Here are the simple mistakes you might be making right now, and how to fix them to reclaim your garden this summer.</p><section class="howto-block">                    <h3>1. Lighting your patio with cool white bulbs</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/k43RJ6AR5bNZvSbkmKWBT4.jpg"                                        alt="Solar Lights"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/k43RJ6AR5bNZvSbkmKWBT4.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>If your outdoor string lights or security spotlights emit a bright, cool-toned white glare, you are essentially signaling to every bug nearby. </p><p>To fix this, Saeed recommends <em> </em>"opting for yellow or warm tones lights with a colour temperature between 2,200K and 2,700K as these are much less likely to invite insects into your garden."</p><p>These specific warm-coloured lights are significantly less attractive to mosquitoes and biting bugs compared to cooler lights above 5,000K, allowing you to actually enjoy your patio without drawing a swarm.</p></p>                </section><section class="howto-block">                    <h3>2. Letting water stagnate in your yard</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/p9WXS6QkkdTzo8kqNZdJJE.png"                                        alt="Bird in clean birdbath"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/p9WXS6QkkdTzo8kqNZdJJE.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Mosquitoes don't just fly in from elsewhere, they are likely breeding right under your nose. Any small pocket of water that sits still for more than a few days becomes a prime nursery for larvae. </p><p>To break the breeding cycle, the experts at Lighting Legends warn that you must remove standing water. "Bugs like mosquitos breed in stagnant water, so eliminating standing water sources can go a long way in helping to reduce their population. </p><p>Regularly check for and remove standing water in flower pots, birdbaths, gutters, and water trays."</p></p>                </section><section class="howto-block">                    <h3>3. Forgetting to plant a natural 'shield'</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/zvhShRMxW6FDGv9Suo7BGm.jpg"                                        alt="How to grow a herb garden"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/zvhShRMxW6FDGv9Suo7BGm.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>You don't need to spray harsh chemicals to keep bugs at bay. Certain plants naturally repel mosquitoes due to the oils and scents they release. </p><p>To build a natural defense barrier around your deck or patio, you're advised to use mosquito-repellent plants: Certain plants naturally repel mosquitoes due to the oils and scents they release. </p><p>"Some of the best plants to use in UK gardens for this purpose include lavender, citronella grass, lemon balm, marigolds and basil."</p></p>                </section><div class="product"><a data-dimension112="9296a01c-812d-11f1-b23a-492403deaa45" data-action="Deal Block" data-label="they double as a great fungus gnat treatment for your plants" data-dimension48="they double as a great fungus gnat treatment for your plants" data-dimension25="$14" href="https://www.amazon.com/Mosquito-Dunks-6s-Biological-Control/dp/B005UTKT0W/ref" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1208px;"><p class="vanilla-image-block" style="padding-top:80.46%;"><img id="QnNqFfwbsRaDmGoURTgqz6" name="Mosquito dunks" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/QnNqFfwbsRaDmGoURTgqz6.png" mos="" align="middle" fullscreen="" width="1208" height="972" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Formulated to release effective levels of BTI for a period of 30 days, this two-pack of mosquito dunks is non-toxic and will kill mosquito larvae before it has the chance to bite. And, as an added bonus, <a href="https://www.tomsguide.com/home/the-mosquito-dunks-that-saved-my-plants-and-my-sanity-from-fungus-gnat-hell-are-on-sale-for-usd17-this-prime-day" data-dimension112="9296a01c-812d-11f1-b23a-492403deaa45" data-action="Deal Block" data-label="they double as a great fungus gnat treatment for your plants" data-dimension48="they double as a great fungus gnat treatment for your plants" data-dimension25="$14">they double as a great fungus gnat treatment for your plants</a>. <a class="view-deal button" href="https://www.amazon.com/Mosquito-Dunks-6s-Biological-Control/dp/B005UTKT0W/ref" target="_blank" rel="nofollow" data-dimension112="9296a01c-812d-11f1-b23a-492403deaa45" data-action="Deal Block" data-label="they double as a great fungus gnat treatment for your plants" data-dimension48="they double as a great fungus gnat treatment for your plants" data-dimension25="$14">View Deal</a></p></div><section class="howto-block">                    <h3>4. Letting your lawn grow wild</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/H4wCeTCUPLUvrzXe9GUsd6.jpg"                                        alt="Mowing grass"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/H4wCeTCUPLUvrzXe9GUsd6.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>If your garden is overgrown, you are giving mosquitoes the perfect place to hide during the hottest parts of the day. They love dark, damp, and sheltered areas to rest until the sun goes down.</p><p>To stop them from nesting in your yard, the experts state that you need to actively maintain your yard. "Dense, overgrown gardens can become a haven for mosquitoes, so keeping your garden neat and tidy should help prevent them."</p><p>Mow your lawn regularly, try and keep your yard free of debris and clutter, and keep those bushes trimmed. </p></p>                </section><section class="howto-block">                    <h3>5. Failing to recruit a backyard security team</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/9nn8ipJxnFkKx6oVH99wm5.jpg"                                        alt="Mosquito on leaf"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/9nn8ipJxnFkKx6oVH99wm5.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>You don't have to fight the mosquito battle entirely on your own. In fact, you might already have a highly effective pest-control team waiting in the wings, you just need to give them a reason to patrol your patio. </p><p>"Birds, bats, and certain insects like dragonflies are natural predators of mosquitoes." Instead of spending cash on chemical sprays, you can easily "attract these animals to your garden by providing nesting boxes, bird feeders, and water sources for them." </p><p>Treat them right, and they will happily clear out the local mosquito population for you completely free of charge.</p></p>                </section><figure class="van-image-figure pull-right inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:676px;"><p class="vanilla-image-block" style="padding-top:31.51%;"><img id="8RpLeaq6UZxfGpTcqGj7HX" name="Google" alt="Google" src="https://cdn.mos.cms.futurecdn.net/8RpLeaq6UZxfGpTcqGj7HX.jpg" mos="" align="right" fullscreen="" width="676" height="213" attribution="" endorsement="" class="pull-rightinline"></p></div></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/how-to-mosquito-proof-yourself-this-summer">How to mosquito-proof yourself this summer — 7 simple tips</a></li><li><a href="https://www.tomsguide.com/home/outdoors/banish-mosquitoes-from-your-home-with-one-natural-ingredient-and-it-also-smells-great">Banish mosquitoes from your home with one natural ingredient — and it also smells great</a></li><li><a href="https://www.tomsguide.com/home/how-to-stop-a-flying-ant-invasion-using-a-single-item-from-your-spice-rack">How to stop a flying ant invasion using a single item from your spice rack</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Older fridges can't keep up with the heat — 3 things to consider before buying a replacement ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/home-appliances/older-fridges-cant-keep-up-with-the-heat-3-things-to-consider-before-buying-a-replacement</link>
                                                                            <description>
                            <![CDATA[ Choosing a fridge doesn't have to be a complicated process, especially if you know the features to prioritise. Here's some expert advice on the 3 things you should look out for. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">kMeXELk3yE8gPS2L6rALge</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/9Lejm44BTTiq5bxmbtS2s3-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 16 Jul 2026 07:55:55 +0000</pubDate>                                                                                                                                <updated>Tue, 21 Jul 2026 11:42:41 +0000</updated>
                                                                                                                                            <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Home]]></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/9Lejm44BTTiq5bxmbtS2s3-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Where should a refrigerator go in the kitchen]]></media:description>                                                            <media:text><![CDATA[Where should a refrigerator go in the kitchen]]></media:text>
                                <media:title type="plain"><![CDATA[Where should a refrigerator go in the kitchen]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/9Lejm44BTTiq5bxmbtS2s3-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>There's no denying it, we're having a heatwave heavy summer in the U.K. But, while we're all struggling to keep cool, there's something else we need to worry about in this heat — our fridges.</p><p>I do my weekly online shop at Tesco, but I recently noticed I wasn't able to get some items because the fridges weren't working. And I'm sure I'm not the only one that's noticed the melting chocolate bars on the shelves. So, if the supermarkets aren't coping, how are our fridges going to keep up at home?</p><p>Well, if you've got an older fridge, it might be bad news. According to Dr Alan Foster, a refrigeration expert who spoke with the <a href="https://www.bbc.co.uk/news/articles/c77yejxnvpxo" target="_blank" rel="nofollow">BBC</a>, a lot of appliances were: "designed decades ago in a much cooler world."</p><p>Most were manufactured to cope with temperatures of up to 32C, and with temperatures rising, they're struggling to function. Some owners may even find their fridges stop working entirely. </p><p>If the current weather has got you considering a replacement fridge, here are the three things you should consider, according to an expert.</p><div class="product"><a data-dimension112="f9dd027c-7eab-11f1-a6f9-e55d076c4a6e" data-action="Deal Block" data-label="The Care HPG2 6513 XK5E Frost Free Multi-door American Fridge Freezer packs in HotPoint's VitalCare technology to recreate the natural daylight cycle to keep your fruits and vegetables fresh for up to seven days. The brand's EvenFlow technology means consistent, cool air is distributed across every shelf to reduce moisture loss while maintaining a stable temperature. The Dual No Frost systems work in both fridge and freezer to automatically prevent ice from forming, so you'll never have to scrape away frost by hand. The touch-sensitive built-in LED display is clear and easy to read, and extra features like the adjustable door storage mean you can configure it to your liking. Additional smart features like Door Open Alarms send you instant notifications on your phone if the fridge door has been left open, and a dedicated holiday mode saves energy costs while you're away. Finally, the fully trained team at AO is more than capable of installing a multitude of products at home for you. The retailer has a wide range of delivery options so it can be delivered on a day that suits you, meaning one less thing for you to worry about." data-dimension48="The Care HPG2 6513 XK5E Frost Free Multi-door American Fridge Freezer packs in HotPoint's VitalCare technology to recreate the natural daylight cycle to keep your fruits and vegetables fresh for up to seven days. The brand's EvenFlow technology means consistent, cool air is distributed across every shelf to reduce moisture loss while maintaining a stable temperature. The Dual No Frost systems work in both fridge and freezer to automatically prevent ice from forming, so you'll never have to scrape away frost by hand. The touch-sensitive built-in LED display is clear and easy to read, and extra features like the adjustable door storage mean you can configure it to your liking. Additional smart features like Door Open Alarms send you instant notifications on your phone if the fridge door has been left open, and a dedicated holiday mode saves energy costs while you're away. Finally, the fully trained team at AO is more than capable of installing a multitude of products at home for you. The retailer has a wide range of delivery options so it can be delivered on a day that suits you, meaning one less thing for you to worry about." data-dimension25="£729" href="https://ao.com/product/hpg26513xk5e-hotpoint-vitalcare-american-fridge-freezer-stainless-steel-107043-27.aspx?cmredirectionvalue=HPG2+6513+XK5E_DI&mm_rf=mm_7acac81f6393843293ec&utm_medium=affiliates&utm_source=Future+Publishing+Limited&utm_campaign=Content%7c103504&utm_content=0&sv_campaign_id=103504&sv_tax1=affiliate&sv_tax2=&sv_tax3=Future+Publishing+Limited&sv_tax4=0&sv_affiliate_id=103504&awc=19526_1783941685_a6e80c3b2e5497885d047c0b32a6c164" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1280px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="vzbWwtt8xGL49mVci8mZcm" name="Hotpoint fridge freezer square" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/vzbWwtt8xGL49mVci8mZcm.jpg" mos="" align="middle" fullscreen="" width="1280" height="1280" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Care HPG2 6513 XK5E Frost Free Multi-door American Fridge Freezer packs in HotPoint's VitalCare technology to recreate the natural daylight cycle to keep your fruits and vegetables fresh for up to seven days. The brand's EvenFlow technology means consistent, cool air is distributed across every shelf to reduce moisture loss while maintaining a stable temperature. The Dual No Frost systems work in both fridge and freezer to automatically prevent ice from forming, so you'll never have to scrape away frost by hand. The touch-sensitive built-in LED display is clear and easy to read, and extra features like the adjustable door storage mean you can configure it to your liking. <br>Additional smart features like Door Open Alarms send you instant notifications on your phone if the fridge door has been left open, and a dedicated holiday mode saves energy costs while you're away. <br>Finally, the fully trained team at AO is more than capable of installing a multitude of products at home for you. The retailer has a wide range of delivery options so it can be delivered on a day that suits you, meaning one less thing for you to worry about.<a class="view-deal button" href="https://ao.com/product/hpg26513xk5e-hotpoint-vitalcare-american-fridge-freezer-stainless-steel-107043-27.aspx?cmredirectionvalue=HPG2+6513+XK5E_DI&mm_rf=mm_7acac81f6393843293ec&utm_medium=affiliates&utm_source=Future+Publishing+Limited&utm_campaign=Content%7c103504&utm_content=0&sv_campaign_id=103504&sv_tax1=affiliate&sv_tax2=&sv_tax3=Future+Publishing+Limited&sv_tax4=0&sv_affiliate_id=103504&awc=19526_1783941685_a6e80c3b2e5497885d047c0b32a6c164" target="_blank" rel="nofollow" data-dimension112="f9dd027c-7eab-11f1-a6f9-e55d076c4a6e" data-action="Deal Block" data-label="The Care HPG2 6513 XK5E Frost Free Multi-door American Fridge Freezer packs in HotPoint's VitalCare technology to recreate the natural daylight cycle to keep your fruits and vegetables fresh for up to seven days. The brand's EvenFlow technology means consistent, cool air is distributed across every shelf to reduce moisture loss while maintaining a stable temperature. The Dual No Frost systems work in both fridge and freezer to automatically prevent ice from forming, so you'll never have to scrape away frost by hand. The touch-sensitive built-in LED display is clear and easy to read, and extra features like the adjustable door storage mean you can configure it to your liking. Additional smart features like Door Open Alarms send you instant notifications on your phone if the fridge door has been left open, and a dedicated holiday mode saves energy costs while you're away. Finally, the fully trained team at AO is more than capable of installing a multitude of products at home for you. The retailer has a wide range of delivery options so it can be delivered on a day that suits you, meaning one less thing for you to worry about." data-dimension48="The Care HPG2 6513 XK5E Frost Free Multi-door American Fridge Freezer packs in HotPoint's VitalCare technology to recreate the natural daylight cycle to keep your fruits and vegetables fresh for up to seven days. The brand's EvenFlow technology means consistent, cool air is distributed across every shelf to reduce moisture loss while maintaining a stable temperature. The Dual No Frost systems work in both fridge and freezer to automatically prevent ice from forming, so you'll never have to scrape away frost by hand. The touch-sensitive built-in LED display is clear and easy to read, and extra features like the adjustable door storage mean you can configure it to your liking. Additional smart features like Door Open Alarms send you instant notifications on your phone if the fridge door has been left open, and a dedicated holiday mode saves energy costs while you're away. Finally, the fully trained team at AO is more than capable of installing a multitude of products at home for you. The retailer has a wide range of delivery options so it can be delivered on a day that suits you, meaning one less thing for you to worry about." data-dimension25="£729">View Deal</a></p></div><h2 id="3-things-to-consider-when-buying-a-new-fridge">3 things to consider when buying a new fridge</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:3993px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8TGbmQTcfBMCUpErYoDCsD" name="Fridge trays - crop.jpg" alt="Fridge shelves" src="https://cdn.mos.cms.futurecdn.net/8TGbmQTcfBMCUpErYoDCsD.jpg" mos="" align="middle" fullscreen="" width="3993" height="2246" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>Buying a new fridge can be a pretty overwhelming purchase. There are so many to choose from and what works for your household might not work for someone else's. Fortunately, we spoke to Ilaria Castagnoli, Product Trainer at <a href="https://www.smeg.com/us" target="_blank" rel="nofollow">SMEG USA</a> to find out her top tips for buying a replacement.</p><h2 id="1-check-the-energy-efficiency">1. Check the energy efficiency</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:4657px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="hLa9rBiNeUji2jXUCyXkXL" name="Energy star rating - crop.jpg" alt="Energy star rating sticker" src="https://cdn.mos.cms.futurecdn.net/hLa9rBiNeUji2jXUCyXkXL.jpg" mos="" align="middle" fullscreen="" width="4657" height="2620" 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>Unsurprisingly, if you're buying a replacement because your fridge can't handle the heat, you'll want to prioritise whether a newer model <a href="https://www.tomsguide.com/home/how-does-the-energy-star-work-and-how-can-it-help-you-save-on-your-utility-bills">is Energy Star rated</a>. Castagnoli says: "These units will be the most energy efficient options on the market, not only decreasing your energy use but also helping you save on your utility bill in the long run."</p><h2 id="2-look-at-different-configurations">2. Look at different configurations</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="QbJGdm9koBX8xHQ2bUe7uB" name="shutterstock_1220416186" alt="Fridge with door open" src="https://cdn.mos.cms.futurecdn.net/QbJGdm9koBX8xHQ2bUe7uB.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>A fridge isn't one-size-fits-all and if you're combining it with a freezer, you've really got to think about what works for you and the other people in your household. "It's also important to think about the type of food you buy and the layout of the unit that will be easiest for you to manage on a daily basis," Castagnoli advises.</p><p>This means taking into consideration whether a bottom or top freezer works best for you, whether you'd prefer a side-by-side unit for easier access, or as Castagnoli also recommends: "Families who like to include ice in every beverage, or access filtered water regularly, should opt for a side-by-side or bottom freezer French door configuration." It all comes down to personal preference and needs.</p><div><blockquote><p>"It's also important to think about the type of food you buy and the layout of the unit that will be easiest for you to manage on a daily basis."</p><p>Ilaria Castagnoli, Product Trainer at SMEG USA</p></blockquote></div><h2 id="3-prioritise-features-for-food-freshness">3. Prioritise features for food freshness</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:4592px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4TrAY5NyA3zoUAGhMA68DK" name="Fridge shelves - crop.jpg" alt="Woman looking inside fridge" src="https://cdn.mos.cms.futurecdn.net/4TrAY5NyA3zoUAGhMA68DK.jpg" mos="" align="middle" fullscreen="" width="4592" height="2583" 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>If you're replacing an older fridge, chances are it hasn't got the features that are available on today's market, which are abundant especially when it comes to food freshness. </p><p>Castagnoli recommends: "Crisper drawers that offer independent temperature and humidity controls are an important consideration because fruits and vegetables often require higher humidity than the main cavity of the refrigerator provides, and different types of produce require different levels of humidity."</p><p>Additional features Castagnoli suggests looking out aside from food freshness include LCD lighting, adjustable shelving, and even whether a model offers a holiday mode. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eBzbQe"></div>                            </div>                            <script src="https://kwizly.com/embed/eBzbQe.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/home/fridge-struggling-in-the-heat-homeowners-urged-to-make-one-change-to-stop-food-from-spoiling">Fridge struggling in the heat? Homeowners urged to make one change to stop food from spoiling</a></li><li><a href="https://www.tomsguide.com/home/beat-the-heat-how-to-stop-your-fridge-from-overheating-this-summer">Beat the heat: How to stop your fridge from overheating this summer</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/people-are-putting-a-piece-of-paper-in-their-fridge-to-see-if-their-home-is-heatwave-ready-and-it-makes-more-sense-than-you-think">People are putting a piece of paper in their fridge to see if their home is heatwave-ready — and it makes more sense than you think</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The Narwal Flow 2 looks slick, but its performance misses the mark for a $1,500 robot vacuum ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/the-narwal-flow-2-looks-slick-but-its-performance-misses-the-mark-for-a-usd1-500-robot-vacuum</link>
                                                                            <description>
                            <![CDATA[ While the Narwal Flow 2 boasts several innovative features, it ultimately fails to deliver the high-quality cleaning performance expected for its premium cost. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">LJu2QtrjHdNAtsn9H6qCRJ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/ecWno7yM29WxwssmBTXyCV-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 15 Jul 2026 07:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></category>
                                                                                                                    <dc:creator><![CDATA[ James Bentley ]]></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/ecWno7yM29WxwssmBTXyCV-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Narwhal Flow 2 robot vacuum in the docking station]]></media:description>                                                            <media:text><![CDATA[Narwhal Flow 2 robot vacuum in the docking station]]></media:text>
                                <media:title type="plain"><![CDATA[Narwhal Flow 2 robot vacuum in the docking station]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/ecWno7yM29WxwssmBTXyCV-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">Narwal Flow 2 robot vacuum: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Wattage:</strong> 65W<br><strong>Base capacity:</strong> 120 days per empty (2.5 litres)<br><strong>Weight:</strong> 11 lbs (33 lb with base station)<br><strong>Suction:</strong> 31,000 Pa<br><strong>Battery:</strong> 7,000 mAh<br><strong>Size: </strong>351.2 mm x 363.5 mm x 95 mm (base 450mm x 360 mm x 530 mm<br><strong>Smart home compatibility: </strong>Yes</p></div></div><p>From smarter tracking to habit observing, the notion of the <a href="https://www.tomsguide.com/us/best-robot-vacuums,review-2000.html">best robot vacuum</a> that can fit into your daily life is certainly an appealing one, and the Narwal Flow 2 promises a lot. </p><p>It offers "unlimited object recognition" alongside smart obstacle avoidance, and even throws in strong suction power and mop functionality with it. But, to get all those nifty little features, Narwal is asking for a lot of cash for its smart robot vacuum, so it has to outperform its cheaper competition.</p><p>I've now spent a few weeks with the Flow 2 making its way around my house (not consecutively, the battery isn't quite that good), and I can say I'm both a little impressed with it and yet paradoxically, underwhelmed. Like the AI you'll find on your phone every day, it is pretty clever, and clearly has the infrastructure to back it up, but I'm not fully convinced it's better than the traditional methods. </p><p>It's a little unproven in its features and too expensive to get the stamp of approval from me, even though I quite like it. Here's how my time with the Narwal Flow 2 went. </p><h3 class="article-body__section" id="section-narwal-flow-2-robot-vacuum-review-price-and-availability"><span>Narwal Flow 2 robot vacuum review: Price and availability </span></h3><p>The standard Narwal Flow 2 is price<a href="https://www.amazon.com/NARWAL-Self-Cleaning-Recognition-Self-Emptying-Tangle-Free/dp/B0GFW4J291" target="_blank" rel="nofollow">d $1499.99 on Amazon.</a></p><p>What's more, there’s a smaller version with an autofill and drainage system that you connect to the water in your house that fetches<a href="https://www.amazon.com/NARWAL-Self-Cleaning-Recognition-Self-Emptying-Tangle-Free/dp/B0GGBH5BKK" target="_blank" rel="nofollow"> $1599.99 on Amazon</a>. </p><h3 class="article-body__section" id="section-narwal-flow-2-robot-vacuum-review-setup"><span>Narwal Flow 2 robot vacuum review: Setup </span></h3><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/ecWno7yM29WxwssmBTXyCV.jpg" alt="Narwhal Flow 2 robot vacuum in the docking station" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/tCzYPJNv6GgTXyeZ9Cdr3U.jpg" alt="The front of the Narwal Flow 2 with its base" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/c4DhGUbYJJY3mU4kpKNckT.jpg" alt="The inside of the Narwal Flow 2 robot vacuum base" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>The Narwal Flow 2 is very easy to set up, even though it’s quite bulky and relatively heavy. Simply take the robot vacuum out of the box, plug the base in, take the extra bits that are stored in the base out, and get it on charge. </p><p>It does come with detergent in the box, so you can pop that in the base for the mop mode. From here, get the app downloaded and let the Flow 2 map your apartment. It managed to pretty much nail the mapping of my apartment on the first go, navigating around chairs, doors, and even human beings. </p><p>From the app, you can tell the Flow 2 to go around, with either the vacuum or mop function, and you can watch it go from room to room, monitoring its progress as it goes. The benefit of this is that you can catch any spots it missed or any locations that have been blocked off by items. The base station holds debris for up to 120 days and has a self-cleaning water tank. </p><p>Set up is pretty smart in a lot of ways. Once you've sent the mop around, the base station will dry it with internal heating to prevent any mould. It's relatively low maintenance, outside of keeping the detergent bin topped up. </p><h3 class="article-body__section" id="section-narwal-flow-2-robot-vacuum-review-design"><span>Narwal Flow 2 robot vacuum review: Design </span></h3><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/sp9vefJCJa6yGXfw4mtYyT.jpg" alt="Narwhal Flow 2 robot vacuum in the docking station" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/BimAgan9qhvxj6JJoTGmwT.jpg" alt="The front of the Narwhal Flow 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ms47JLdE5uqyR7Utq4254U.jpg" alt="The Narwhal Flow 2 from the top" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>The Narwal Flow 2 has a lot going for it. The AI functionality helps it spot debris and map the home, but it's also used in voice controls, which means you rarely have to control it from the app if you don't want to. That AI functionality also makes the Flow 2 swap between a loud or light vacuum depending on the surface and the level of cleaning each surface needs.</p><p>Oddly, you can entirely control the robot vacuum from your phone, using its built-in webcam. This is a decent way of keeping track of the home while you're away, and its speakers mean you can actually talk to people through it. This mostly ended in me chatting to my partner from afar, but I can see some practical uses here, like chatting to pets, or even just checking if you've left the oven on. </p><p>It has a pet mode for finding your pets, and you can cycle through vacuum power intensities, should you feel a more thorough clean is needed. It's very neat, though very niche. </p><p>The Flow 2 sells itself on being effortless, and this is partially done in how it deals with waste, but also in some smart software. It has systems to reduce battery wear over time (which is certainly welcome with the 1-2 hours of battery life), and it can technically recognise an infinite number of obstacles, which is an infinite amount higher than the 200 the Flow can pick up. </p><h3 class="article-body__section" id="section-narwal-flow-2-robot-vacuum-review-performance"><span>Narwal Flow 2 robot vacuum review: Performance</span></h3><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/2neUZwwpYYA4cgNNnhsb8U.jpg" alt="The Narwal Flow 2 showing light on hair on a carpet" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/PBoYY9UFuemCWSBZVs5kTG.jpg" alt="The Narwal Flow 2 picking up hair on the ground" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/xo92dZzfD7XBYkXPhc5jRG.jpg" alt="Black Narwhal Flow robot vacuum on a carpet" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>This is the most important part of using a robot vacuum, and, unfortunately, performance is pretty mixed for me when considering the price.</p><p>In our testing suite, it entirely failed to pick up many Cheerios scattered around a confined space. It would regularly push them around the floor or crush them under its wheels. A few of them got stuck behind the wheels, and I’m not entirely convinced I’ve got them all out even now. </p><p>This means similarly sized debris simply won’t be picked up by the Flow 2. It’s not entirely uncommon that you would drop a piece of cereal while moving with a bowl or any other food, and the Flow 2 is more likely to squish it into the floor than pick it up.</p><p>I'm not entirely sure if this is a physical problem in how it picks up debris between its brush or with the AI tracking failing to register that it needs to give extra juice to the engine at the right point, but my money is on the latter. </p><p>When it comes to picking up hair, it does decently. On a low-pile carpet, it needs to go over the same patch multiple times to pick it up, and it won’t necessarily do so naturally, which means having to send it around again. I have managed to get it to pick up everything in a single go, but there's no magic touch here. Sometimes it does, sometimes it doesn't </p><p>On tile or otherwise flat floor, its 31,000 PA suction power is more than enough to get up any dirt that gets into its path. If you’re thinking of getting a Flow 2 for a very tile-heavy apartment, it will likely perform to your expectations, and its mop mode is solid too. </p><p>It's intuitive and looks after itself. I did find it a little better at getting specific stains than just generally mopping an area, though, which I think is partly what the vacuum is going for in the first place. </p><p>Kitty litter was picked up absolutely fine in a single go, and with no issues. For everyday dirt, the Flow 2 does just fine. </p><p>Using it week to week, the Flow 2 certainly cuts down on the amount of manual vacuuming I have to do, but it’s also significantly more expensive than equally viable performers, even at its discount price.</p><div ><table><caption>Results table</caption><tbody><tr><td class="firstcol empty" ></td><td  ><p>Overall</p></td><td  ><p>Cheerios</p></td><td  ><p>Kitty Litter</p></td><td  ><p>Pet Hair</p></td></tr><tr><td class="firstcol " ><p>Hardwood floor</p></td><td  ><p>76</p></td><td  ><p>30</p></td><td  ><p>100</p></td><td  ><p>100</p></td></tr><tr><td class="firstcol " ><p>Carpet</p></td><td  ><p>66</p></td><td  ><p>20</p></td><td  ><p>100</p></td><td  ><p>80</p></td></tr></tbody></table></div><p>I also found the mapping system to be good, but it struggled occasionally with finding its own base. When I moved it ever so slightly, the Flow 2 vacuum struggled to make adjustments to find its new home (just inches across). </p><p>One place the Flow 2 really does struggle is with corners and edges. The robot vacuum is pretty big and doesn't have edge-to-edge coverage, so you will likely have to go over these yourself. This is a tad disappointing for such a smart vacuum, though not entirely unheard of. </p><h3 class="article-body__section" id="section-narwal-flow-2-robot-vacuum-review-verdict"><span>Narwal Flow 2 robot vacuum review: Verdict </span></h3><p>I really like the Narwal Flow 2 - in concept. It is definitely smart, and it has tonnes of niche little features I think are just plain neat. </p><p>However, when you're approaching north of $1,000, neat doesn't quite cut it, especially when it struggles with some of the fundamentals. </p><p>As an additive tool to my regular hand vacuuming, the Flow 2 certainly fits in a good spot, and I didn't find my flooring to be very dirty after a few weeks of using it, but there are cheaper options that get similar results. </p><p>The Flow 2 is certainly a bit better than budget robot vacuums I've tried, but is it three times better? No, not nearly. That doesn't mean it doesn't do its job; it's just not offering a price-to-dollar ratio that is as smart or efficient as its software. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Why your peace lily isn't blooming — and the exact feeding method to fix it ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/how-to-fertilize-a-peace-lily-for-lush-leaves-and-constant-blooms</link>
                                                                            <description>
                            <![CDATA[ The right fertilizer strategy keeps your peace lily thriving year-round. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">VRhTB2pFhZJjXwDp6vbHLS</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/XEFaLHTxhyqmM6boZTwWTB-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 15 Jul 2026 05:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                <author><![CDATA[ kaycee.hill@futurenet.com (Kaycee Hill) ]]></author>                    <dc:creator><![CDATA[ Kaycee Hill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xHn6RmpEqg87cvtLwrBu9G.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/XEFaLHTxhyqmM6boZTwWTB-1280-80.jpg">
                                                            <media:credit><![CDATA[Getty Images]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Watering a peace lily]]></media:description>                                                            <media:text><![CDATA[Watering a peace lily]]></media:text>
                                <media:title type="plain"><![CDATA[Watering a peace lily]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/XEFaLHTxhyqmM6boZTwWTB-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Peace lilies are forgiving houseplants <a href="https://www.tomsguide.com/home/gardening/the-one-place-you-should-never-put-a-peace-lily-and-why">until they're not</a>. They tolerate neglect, bounce back from underwater watering, and look elegant in corners where other plants struggle. </p><p>But they have one non-negotiable requirement: regular feeding. Without it, those glossy green leaves <a href="https://www.tomsguide.com/home/why-are-the-flowers-on-my-peace-lily-turning-brown-i-asked-an-expert-what-i-should-do">start losing their shine</a>, blooms become sparse, and your plant quietly declines into a sad, pale version of itself. </p><p>Get the timing and amount right, and your peace lily rewards you with vibrant foliage and those signature white flower spathes appearing constantly. Here's how to feed your plant so it actually thrives.</p><section class="howto-block">                    <h3>Feed during growth season</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/xxW2FQFAdFPwn6noiGRepQ.jpg"                                        alt="Peace lily plant close up "                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/xxW2FQFAdFPwn6noiGRepQ.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Peace lilies grow aggressively in spring and summer when days are longer and sunlight is abundant. <strong>This is when they need regular feeding</strong>, typically starting in late February or early March. </p><p>During these months, your plant actively produces new leaves and flowers, a process that demands consistent nutrients.</p><p>Conversely, you should <strong>stop fertilizing entirely in late fall and winter</strong>. Your plant receives less sunlight, grows slowly, and enters a natural dormancy where it needs far less nutrition. </p><p>If you use grow lights year-round, you can fertilize continuously since artificial light mimics constant growing conditions. But for most indoor gardeners, seasonally adjusting fertilizer prevents accidentally overfeeding the root system.</p></p>                </section><section class="howto-block">                    <h3>Option 1: Fast-acting water-soluble fertilizer</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/Usz7J2Fa5XZPmD8kJuDHcG.jpg"                                        alt="Watering a peace lily"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/Usz7J2Fa5XZPmD8kJuDHcG.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Water-soluble fertilizers give you immediate control and are perfect if you enjoy a hands-on routine. For peace lilies, <strong>look for a balanced liquid fertilizer with an equal nutrient ratio, such as a 10-10-10 NPK mix</strong>. </p><p>During the active spring and summer growing season, <strong>you will want to apply this mixture every two to four weeks</strong>.</p><p>To apply it correctly, <strong>mix the fertilizer into your watering can, but dilute it to half the strength recommended</strong> on the product packaging. Weak fertilizer applied consistently produces beautiful results without risking root burn. </p><p>Pour the mixture evenly around the base of the plant just like a regular watering, ensuring it saturates the soil without splashing directly onto the glossy leaves.</p></p>                </section><section class="howto-block">                    <h3>Option 2: Low-maintenance slow-release granules</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/hjMM8nP3Q988Tbo3VmpkAo.jpg"                                        alt="Peace lily refreshed with pruned brown flowers removed"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/hjMM8nP3Q988Tbo3VmpkAo.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Future / Camilla Sharman)</div></figure>                    <p><p>If you tend to forget routine plant chores, slow-release granules offer a highly forgiving alternative. These small pellets are designed to break down slowly, steadily releasing nutrients into the soil every time you water over the course of several months.</p><p>Because of this sustained release<strong>, you only need to apply them once at the start of the spring growing season</strong>.</p><p>To apply them,<strong> scatter the recommended amount of granules evenly across the top of your potting mix</strong>. For the best results, <strong>gently work the pellets into the top inch of soil using a kitchen fork</strong> so they are well integrated. </p><p>Once scratched into the dirt, they will completely automate your feeding schedule for the rest of the summer, eliminating any need for mixing or measuring liquids.</p></p>                </section><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OLq6JX"></div>                            </div>                            <script src="https://kwizly.com/embed/OLq6JX.js" async></script><figure class="van-image-figure pull-right inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:676px;"><p class="vanilla-image-block" style="padding-top:31.51%;"><img id="ZCwuUFRE3YeboHR4zyEWmb" name="r3t8zZ5ve4GewFTeoCM3R6" alt="Google" src="https://cdn.mos.cms.futurecdn.net/ZCwuUFRE3YeboHR4zyEWmb.jpg" mos="" align="right" fullscreen="" width="676" height="213" attribution="" endorsement="" class="pull-rightinline"></p></div></div><figcaption itemprop="caption description" class="pull-right inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></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/gardening/where-to-put-a-peace-lily-so-it-actually-flowers-5-ideal-spots-in-your-home">Where to put a peace lily so it actually flowers — 5 ideal spots in your home</a></li><li><a href="https://www.tomsguide.com/home/gardening/peace-lily-outgrowing-its-pot-heres-when-and-how-to-repot-it">Peace lily outgrowing its pot? Here's when and how to repot it</a></li><li><a href="https://www.tomsguide.com/home/gardening/your-peace-lily-needs-dividing-if-you-see-these-signs-heres-how-to-do-it">Your peace lily needs dividing if you see these signs — here's how to do it</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ IKEA's new collection is designed for frequent movers — I'm excited about how portable and affordable it is ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/ikeas-new-collection-is-designed-for-frequent-movers-im-excited-about-how-portable-and-affordable-it-is</link>
                                                                            <description>
                            <![CDATA[ IKEA's new collection is designed 'for life in transit', meaning if you move a lot and you're tired of leaving furniture on the curb, there's a stylish solution. Here's a sneak peek at IKEA's easy-to-move furniture. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ME7wtwnMbkN9J7KBEEnzKU</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/MEb2H4NxGCadPqLXWYRWXk-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 14 Jul 2026 13:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></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/MEb2H4NxGCadPqLXWYRWXk-1280-80.jpg">
                                                            <media:credit><![CDATA[IKEA]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A red metal bookend from IKEA filled with books]]></media:description>                                                            <media:text><![CDATA[A red metal bookend from IKEA filled with books]]></media:text>
                                <media:title type="plain"><![CDATA[A red metal bookend from IKEA filled with books]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/MEb2H4NxGCadPqLXWYRWXk-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Over the years, I've found myself returning to IKEA any time I'm kitting out a new place, doing a home refresh, or looking to upgrade on some home essentials. Not only does it offer affordable furnishings, but they're also fun to make (if you like building flat-pack, like me). But, how about if you could also pack them back up when it's time to move?</p><p>IKEA's newest collection is specially designed with movers in mind. <a href="https://www.ikea.com/es/en/new/kompishaeng-a-collection-that-was-made-to-be-moved-pub82d35c54/" target="_blank" rel="nofollow">KOMPISHÄNG</a> is an 11-piece collection for "a growing generation of young city dwellers and college students who move often and settle in quickly."</p><p>I used to move <em>a lot</em> and I had a tendency to just leave the furniture behind, rolling my two suitcases away unencumbered. IKEA's new collection aims to eliminate the need to start over, crafting items that are both portable and multifunctional. It launches online on August 15, but you can also find it exclusively in stores from July 13 to August 14. Here's a sneak peek at what's on offer.</p><h2 id="for-life-in-transit">For life in transit</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="nSJWgzbxKvB4oYogPfGeek" name="ikea_collection_3" alt="People carry IKEA's new KOMPISHÄNG furniture collection on an elevator" src="https://cdn.mos.cms.futurecdn.net/nSJWgzbxKvB4oYogPfGeek.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: IKEA)</span></figcaption></figure><p>IKEA's no stranger to curating collections with a purpose. Its <a href="https://www.tomsguide.com/home/ikeas-new-outdoor-collection-starts-at-just-usd4-and-ive-already-got-my-eye-on-the-affordable-grill">SOLUPPGÅNG range</a> is geared towards those who love exploring the great outdoors. Its <a href="https://www.tomsguide.com/home/gardening/ikeas-latest-collection-is-a-plant-parents-dream-and-it-starts-at-just-usd9">DOFTRIPS collection</a> is for those who love to grow and nurture plants. And now, the KOMPISHÄNG collection is designed for those who tend to move around, whether for work, travel, study, or another life purpose. </p><p>IKEA's newest collection wants to make sure you can pick up pieces for your residence that are still purposeful without breaking the budget. And more importantly, they're pieces that travel incredibly well when it's time to go again.</p><div><blockquote><p>Even if you move often, you don't want your home to feel temporary.</p><p>Dora Ding, Product Developer at IKEA</p></blockquote></div><p>Made from solid pine, powder-coated steel and durable canvas, this entire range has been designed to be packed, carried and set up again in a new location with ease. But, it'll still look as good when you finally move it into a home you want to stay in. You just won't have had such a hard time getting it there.</p><p>And IKEA has done its research. Spending time with young adults aged 20-28 in London, it wanted to find out patterns and needs for its newest collection. As Dora Ding, Product Developer at IKEA explains: "Even if you move often, you don't want your home to feel temporary. We saw how carefully people chose the things they carried with them from place to place, and that insight shaped the collection. We want the collection to be portable but still have a permanent look, people should feel settled and [their space] considered."</p><h2 id="curated-for-convenience">Curated for convenience</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="gXsYZTksRoU97cwgfE4bek" name="ikea_collection_2 (1)" alt="An IKEA side table and jute plant pot in a room" src="https://cdn.mos.cms.futurecdn.net/gXsYZTksRoU97cwgfE4bek.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: IKEA)</span></figcaption></figure><p>So, what is included in the 11-piece collection? Well, it's composed of a solid pine desk, stools that fit together like a puzzle, a hanging wardrobe organizer, solid pine mirror frames, a shopping bag on wheels, side table, door hangers (one pine, one canvas), a triangular back cushion, jute plant pot and red metal bookend. </p><p>Each of the 11 pieces has been designed with portability in mind. The solid pine desk, available for $99.99, is one of the pieces I'm most excited about. Not only does it require no tools or assembly time to set up, just two pulls and two clicks, but it even has a built-in handle under the tabletop making it incredibly easy to move around.</p><p>The hanging wardrobe organizer is another interesting design, converting from wardrobe to carry bag with a single zip that can be worn on your back. That means you can leave your clothes inside, zip it up and move to the next place without the stress of packing and unpacking. </p><p>And the side table can easily turn into a shoulder table in seconds, folding completely flat and making it easy to fit anywhere. As David Wahl, Designer at IKEA explains: "This table isn't just a temporary solution. It's designed to accompany life's transitions – equally at home in a first apartment, a family house, or even a retirement space."</p><p>I'm already thinking about what I'll pop in my basket when the KOMPISHÄNG collection launches. I just wish it had existed when I was still living the nomad life.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ORKDNX"></div>                            </div>                            <script src="https://kwizly.com/embed/ORKDNX.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/home/smart-home/ikeas-new-wireless-charger-looks-like-a-donut-and-costs-usd9-but-its-surprisingly-capable">IKEA's new wireless charger looks like a donut and costs $9, but it's surprisingly capable</a></li><li><a href="https://www.tomsguide.com/home/i-organize-my-home-with-these-ikea-storage-items-and-its-never-looked-better">I just upgraded my home with these 5 IKEA items</a></li><li><a href="https://www.tomsguide.com/home/how-to-paint-your-ikea-furniture-for-a-flawless-finish-decorating-experts-share-their-top-tips">How to paint your IKEA furniture for a flawless finish — decorating experts share their top tips</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Why is my grass patchy after seeding? I asked the experts how to fix it ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/why-is-my-grass-patchy-after-seeding-i-asked-the-experts-how-to-fix-it</link>
                                                                            <description>
                            <![CDATA[ Do you have patches ruining your lawn? Gardeners reveal the reason your lawn is still patchy after seeding it and how to fix it. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">2Hw5n8AVgynPsYtWmGPXnn</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/DNe9kwrtK3EdUoANa8LTJW-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 12 Jul 2026 05:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Lawn Care]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                                    <dc:creator><![CDATA[ Cynthia Lawrence ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/CCuSgQpd5NyZ46CgoF9cva.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/DNe9kwrtK3EdUoANa8LTJW-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Holding grass seeds to plant]]></media:description>                                                            <media:text><![CDATA[Holding grass seeds to plant]]></media:text>
                                <media:title type="plain"><![CDATA[Holding grass seeds to plant]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/DNe9kwrtK3EdUoANa8LTJW-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Now that summer has arrived, the scorching temperatures often take their toll on our previously lush and <a href="https://www.tomsguide.com/how-to/how-to-make-grass-greener">green grass</a>. It’s disheartening to see unappealing <a href="https://www.tomsguide.com/home/gardening/why-do-i-have-yellow-patches-in-my-lawn-gardening-experts-share-their-top-tips-on-how-to-fix-it">yellow patches</a> or spots on your lawn, particularly when you have gone the extra mile to seed bare spots.</p><p>In addition, you’re probably wondering why grass grows at a faster rate in some bare spots and not in others. Luckily, I’ve called on the lawn care experts to reveal why my grass is still patchy after seeding and to share tips on how to fix it.</p><h2 id="what-causes-lawn-to-be-patchy-after-seeding">What causes lawn to be patchy after seeding?</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:5390px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RwnyCYupmo4CsDXuzHHtpJ" name="shutterstock_2249537999.jpg" alt="Brown spots in grass in garden" src="https://cdn.mos.cms.futurecdn.net/RwnyCYupmo4CsDXuzHHtpJ.jpg" mos="" align="middle" fullscreen="" width="5390" height="3032" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Brown patches in grass </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>Essentially, overseeding is when you spread a thin layer of grass seed that matches your existing lawn type and climate zone. Experts usually suggest this method to fix bare spots, naked turf, or overrunning weeds, which are clear signs of density issues. </p><p>But why does your grass still look a little patchy even after you’ve seeded? According to experts, there could be several reasons for this — from birds swiping seeds off the ground to the breeze blowing some away. </p><p>“It’s quite common for the first go at seeding to come up slightly patchy,” adds Chrissie Handley, lawn care specialist at <a href="http://www.onlineturf.co.uk" target="_blank">Online Turf</a>.  “You tend to lose some seed either from the birds picking some off the ground, or through wind blowing it away, or heavy rain can also wash some away, giving you a sparse regrowth.”</p><p>Although it’s advisable to sprinkle the seeds by hand to tackle those smaller areas, you may be spreading it too thin in certain areas — leading to patchy turf after seeding. </p><div><blockquote><p>"You tend to lose some seed either from the birds picking some off the ground, or through wind blowing it away"</p><p>Chrissie Handley, lawn care specialist </p></blockquote></div><p>“Sometimes, the area can appear sparse and patchy if you’ve seeded by hand, as sometimes there will be a higher concentration of seed in certain areas, depending on the way it fell when sown,” adds Handley. </p><p>“You can simply top up these areas with additional seed once you begin to notice the sparse areas. If you do this when the seed is just beginning to germinate, you’ll have a better chance of it blending together quickly.”</p><h2 id="how-do-i-fix-my-patchy-lawn">How do I fix my patchy lawn?</h2><p>So what can I do to fix unsightly patches? Experts advise the best solution is to reseed the affected areas, using lawn repair products or a good fertilizer.</p><p>“Proper fertilization also encourages dense turf, effectively crowding out weeds and minimizing bare patches, reducing soil erosion and promoting overall lawn stability,” states Allison Koenig, lawn and garden expert at <a href="http://tractorsupply.com" target="_blank">Tractor Supply</a>. </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:3148px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="tyBNke4AjR6gYGGH8NUiDZ" name="shutterstock_484448059.jpg" alt="Brown patches in grass" src="https://cdn.mos.cms.futurecdn.net/tyBNke4AjR6gYGGH8NUiDZ.jpg" mos="" align="middle" fullscreen="" width="3148" height="1771" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Brown patches in grass </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>“Good fertilizing practice can help ensure you grow greener, healthier grass with a strong root system to combat heat, cold, drought, mowing, and foot traffic.”</p><p>For top tips, check out <a href="https://www.tomsguide.com/how-to/how-often-should-you-fertilize-your-lawn">how often should you fertilize your lawn and when to do it.</a></p><p>Of course, prevention is key, and there are top tips you can do before seeding to ensure you get lush, revived grass for the summer. These include things sowing at the right time of day or adding a layer of topsoil afterwards.  </p><p>“There are ways to try and counteract this with the way that you sow – always factor in extra, it’s always better to overseed to over-prepare for any that may be lost, as well as covering the seeded area,” Handley suggests.</p><p>“Add in a thin layer of topsoil afterwards. This can help stop the birds from picking it off, and also helps the seed make good contact with the ground, improving your germination rate. </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="zD5WqVZfvzmsc6ECA6k7ac" name="shutterstock_1700730595edit" alt="Grass seed being sown over thin and patchy grass" src="https://cdn.mos.cms.futurecdn.net/zD5WqVZfvzmsc6ECA6k7ac.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>“Make sure it’s a light covering of topsoil so that you’re not smothering them, and try to sow on a mild day where temperatures are forecast to be consistent for a while for a better yield. You should also keep the area consistently moist but don’t overwater — if you’re getting a lot of rain, hold off on watering.”</p><p>Typically, you should ensure your lawn gets the recommended one to two inches of water each week, but during hot, dry spells, you may need to double the duration. Additionally, watering deeply every three to four days rather than daily helps promote stronger, more drought-resistant roots. </p><p>For more top tips, check out <a href="https://www.tomsguide.com/home/lawn-care/how-to-tell-if-your-lawn-needs-overseeding-3-signs-you-cant-ignore">how to tell if your lawn needs overseeding</a>, especially if you want to maintain healthy grass all year round. Or if you just want a lush lawn, try these <a href="https://www.tomsguide.com/home/lawn-care/11-secrets-to-a-lawn-thats-greener-than-your-neighbors">11 secrets to a lawn that’s greener than your neighbor's.</a></p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eG0N2W"></div>                            </div>                            <script src="https://kwizly.com/embed/eG0N2W.js" async></script><h3 class="article-body__section" id="section-lawncare-essentials-we-love"><span>Lawncare essentials we love</span></h3>        <div class="featured_product_block featured_block_hero" data-id="fbaf02ea-7c4d-11f1-96f3-ddd2fd156b01">            <a href="https://www.amazon.com/Garden-Guru-Handheld-Spreader-Shaker/dp/B0F8P8QM3V/ref=sr_1_8?" data-model-name="Garden Guru Handheld Seed Spreader Shaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:82.80%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ezrwVY6BYsgBZtBcNhMTm9.jpg" alt="Garden Guru Handheld Seed Spreader Shaker – Multi Use Grass Seed, Fertilizer, Salt, Ice & Snow Melt Spreader – Adjustable Twist Cap W Multiple Size Openings – Lightweight 1l Capacity Hand Seeder"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon</div>                                        <div class="featured__title">Garden Guru Handheld Seed Spreader Shaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="fbaf0358-7c4d-11f1-bc16-ab53e81a0be3">            <a href="https://www.amazon.com/dp/B07BD2GZW8" data-model-name="Garden Gloves" 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/YyBJP4YUpLVNV4q2yKFfjS.jpg" alt="Amazing Stuff For You green and gray gardening gloves"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazing Stuff</div>                                        <div class="featured__title">Garden Gloves</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="fbaf03c6-7c4d-11f1-bc38-fd8c1a23debe">            <a href="https://www.amazon.com/Grampas-Weeder-CW-01-Original-Remover/dp/B001D1FFZA?" data-model-name="Grampa's Weeder" 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/kP7pgu2tpxz8d34HtVQgxC.jpg" alt="Grampa's Weeder The Original Stand Up Weed Puller Tool with Long Handle"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Grampa's Weeder</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="fbaf0434-7c4d-11f1-97e4-6fa534c6c39f">            <a href="https://www.amazon.com/Greenworks-G-MAX-Mower-Batteries-Charger/dp/B086K59Q79/ref=sr_1_3?" data-model-name="Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower" 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/8LF2UvRs8EXvmebaZefwzA.jpg" alt="Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower (200+ Compatible Tools), (2) 4.0ah Batteries and Dual Port Rapid Charger Included"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon</div>                                        <div class="featured__title">Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower</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/how-to/7-common-lawn-care-mistakes-you-are-probably-making-right-now"><u>7 common lawn care mistakes you’re probably making right now</u></a></li><li><a href="https://www.tomsguide.com/how-to/9-ways-to-get-the-most-out-of-your-lawn-mower">9 ways to get the most out of your lawn mower</a></li><li><a href="https://www.tomsguide.com/how-to/5-plants-that-will-keep-wasps-out-of-your-yard">5 plants that will keep wasps out of your yard</a></li></ul>
                                                            </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>
                                                                                                                                                                                                <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 &amp; 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-2">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="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." data-dimension48="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." 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>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="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." data-dimension48="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." data-dimension25="$9.19">View Deal</a></p></div><h2 id="here-s-what-happened-when-i-tried-it-2">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[ You could be making a huge mistake with your bird feeder — here's what you should be doing instead ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/you-could-be-making-a-huge-mistake-with-your-bird-feeder-heres-what-you-should-be-doing-instead</link>
                                                                            <description>
                            <![CDATA[ I love to feed the birds that visit my garden, but I didn't realize the way I've been doing it could be causing more harm than good. Here's the quick change you might want to think about making. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">SCCXtdYWkXy7WfQeZsLkka</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/x4t2QWy3A6q7AJRtzwg2Bc-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 07 Jul 2026 05:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></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/x4t2QWy3A6q7AJRtzwg2Bc-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A bird feeder in a garden]]></media:description>                                                            <media:text><![CDATA[A bird feeder in a garden]]></media:text>
                                <media:title type="plain"><![CDATA[A bird feeder in a garden]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/x4t2QWy3A6q7AJRtzwg2Bc-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I've been trying to get the right bird feeder for my garden for months. From my hanging cage that attracts unwanted attention from squirrels, to the smaller wooden box that keeps falling down, to the DIY one my son made that came under attack, I'm not having much luck.</p><p>So, I turned to my old faithful — a flat bird feeder that balances safely upon a pole and provides a roof to shield the food from the elements. Only, it turns out, that's exactly what I <em>shouldn't</em> be using. While I thought I was doing something good for my local wildlife with the most stable option, in fact, using a flat bird feeder could be having the opposite effect. And if you're using one too, you might want to rethink it.</p><h2 id="why-you-shouldn-t-be-using-a-flat-bird-feeder">Why you shouldn't be using a flat bird feeder </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="Qkg9M4RYEaiP8wCt334szU" name="shutterstock_2041973156 (1)" alt="Hawfinches on bird feeder" src="https://cdn.mos.cms.futurecdn.net/Qkg9M4RYEaiP8wCt334szU.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>Bird tables feel like one of the most common ways to feed birds that are local to your area and that's because they are. But, conservation charities like Britain's Royal Society for the Protection of Birds (<a href="https://www.rspb.org.uk/whats-happening/news/how-to-help-garden-birds" target="_blank">RSPB</a>) <em>strongly</em> advise against them and there's a good reason for it.</p><p>According to the RSPB, it's actually a hot bed for disease, specifically finch trichomonosis, a highly contagious variant that's easily spread around bird feeders. And the problem with flat bird feeders in particular is that the feed sits out in one spot for extended periods of time where birds continuously visit and revisit it.</p><p>RSPB specifically advises: "Don't use bird feeders with flat surfaces, such as bird tables, window feeders and feeders with trays. Research has confirmed that there's a higher risk of disease spreading on flat surfaces, where contaminated food can collect."</p><p>Leaving diseased food or droppings on a flat surface can have devastating consequences for your garden birds, so instead of using flat bird feeders, you should be avoiding them.</p><h2 id="what-you-should-do-instead">What you should do instead</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="gfNwxSEv9kfGZBgEQU8CBc" name="bird_feeder_2" alt="A bird feeder in a garden" src="https://cdn.mos.cms.futurecdn.net/gfNwxSEv9kfGZBgEQU8CBc.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>Fortunately, both the RSPB and The National Audubon Society have abundant advice on what you should do instead if, like me, you're struggling to find the right solution for feeding your garden birds.</p><p>The <a href="https://www.audubon.org/news/three-easy-important-ways-keep-your-bird-feeder-disease-free" target="_blank">National Audubon Society </a>recommends that to prevent the spread of illness, you need to follow three simple steps: "Clean feeders regularly, tidy below the feeder and share the wealth." The <a href="https://www.usgs.gov/centers/national-wildlife-health-center" target="_blank">National Wildlife Health Center</a> advises that to clean them, you should make "a solution of nine parts water to one part bleach," making sure to dry the bird feeder fully before putting it back out.</p><p>Tidying below it is also important, as well as to "share the wealth" by spreading out food among a few feeders to hopefully reduce the chance of all of them becoming contaminated.</p><p>Overarching all this advice, the RSPB says that what you should really be doing is to "help birds find their own food." It recommends instead that you should be helping garden birds to discover foods by planting bird-friendly plants in your garden, including sunflowers and ivy.</p><p>If you want to use bird feeders, it seems choosing hanging ones are your best option, as long as you're keeping them clean and moving them around regularly so that food build-up doesn't occur. I'll just make sure that next time I buy a hanging bird feeder, it's one that deters squirrels.</p><div class="product"><a data-dimension112="912a4cf8-0109-4613-a790-70d33712cd93" data-action="Deal Block" data-label="Hanging bird feeders are great, but squirrels are really good at getting onto them. Fortunately, you can buy specific models, like this from Droll Yankees, that are designed just for birds with no squirrel intruders. A rotating perch and UV-stabilized seed tube are both included to make this possible." data-dimension48="Hanging bird feeders are great, but squirrels are really good at getting onto them. Fortunately, you can buy specific models, like this from Droll Yankees, that are designed just for birds with no squirrel intruders. A rotating perch and UV-stabilized seed tube are both included to make this possible." data-dimension25="$94.99" href="https://www.amazon.com/Droll-Yankees-DROFLIPPER-887961451702-Squirrel/dp/B00004ZB4U" 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="S69MNmuVnMyWx5z2BxvWsj" name="bird_feeder_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/S69MNmuVnMyWx5z2BxvWsj.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Hanging bird feeders are great, but squirrels are really good at getting onto them. Fortunately, you can buy specific models, like this from Droll Yankees, that are designed just for birds with no squirrel intruders. A rotating perch and UV-stabilized seed tube are both included to make this possible.<a class="view-deal button" href="https://www.amazon.com/Droll-Yankees-DROFLIPPER-887961451702-Squirrel/dp/B00004ZB4U" target="_blank" rel="nofollow" data-dimension112="912a4cf8-0109-4613-a790-70d33712cd93" data-action="Deal Block" data-label="Hanging bird feeders are great, but squirrels are really good at getting onto them. Fortunately, you can buy specific models, like this from Droll Yankees, that are designed just for birds with no squirrel intruders. A rotating perch and UV-stabilized seed tube are both included to make this possible." data-dimension48="Hanging bird feeders are great, but squirrels are really good at getting onto them. Fortunately, you can buy specific models, like this from Droll Yankees, that are designed just for birds with no squirrel intruders. A rotating perch and UV-stabilized seed tube are both included to make this possible." data-dimension25="$94.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/ditch-the-feeder-this-summer-the-natural-way-to-keep-garden-birds-healthy">Ditch the feeder this summer: The natural way to keep garden birds healthy</a></li><li><a href="https://www.tomsguide.com/home/why-your-birdfeeder-becomes-a-disease-spot-in-a-heatwave-and-how-to-fix-it">Why your birdfeeder becomes a ‘disease spot’ in a heatwave — and how to fix it</a></li><li><a href="https://www.tomsguide.com/cameras-photography/ive-been-using-this-birdfy-smart-bird-feeder-for-four-months-heres-what-ive-learned">I’ve been using this Birdfy smart bird feeder for four months: here’s what I've learned</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Amazon just discounted some of the best coolers for summer — save up to 35% on YETI, Ninja, Coleman and more ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/outdoors/amazon-just-discounted-some-of-the-best-coolers-for-summer-save-up-to-35-percent-on-yeti-ninja-coleman-and-more</link>
                                                                            <description>
                            <![CDATA[ Some of the best coolers we've reviewed are seeing price drops this summer! Check out the deals I recommend from top brands. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">mafqW4Q3EpUVzj3LY3FVYe</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/fBcQvCo2N2YuDaHkh6gxTa-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 06 Jul 2026 17:11:01 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Outdoors]]></category>
                                                    <category><![CDATA[Home]]></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/fBcQvCo2N2YuDaHkh6gxTa-1280-80.jpg">
                                                            <media:credit><![CDATA[Amazon / edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Cooler deals]]></media:description>                                                            <media:text><![CDATA[Cooler deals]]></media:text>
                                <media:title type="plain"><![CDATA[Cooler deals]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/fBcQvCo2N2YuDaHkh6gxTa-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Summer is in full swing — and whether you're heading out for a picnic, a roadtrip or a beach vacation, you'll likely need a hand in keeping your snacks and drinks perfectly cool. That's why now is the perfect time to invest in one of the <a href="https://www.tomsguide.com/best-picks/best-coolers">best coolers</a>.</p><p>Even better? A few of our favorites are <a href="https://www.amazon.com/camp-coolers/b/ref=dp_bc_5?ie=UTF8&node=14335431" target="_blank" rel="nofollow">up to 35% off on Amazon</a>. If you're not sure where to begin, I've done the heavy lifting for you and compiled the best of the best cooler deals below. They come courtesy of top brands like Ninja, Igloo, Coleman, YETI and more. </p><p>Keep scrolling to see all the cooler deals I recommend! You can also check out our <a href="https://www.tomsguide.com/coupons/amazon.com">Amazon promo codes</a> page.</p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><a href="https://www.amazon.com/camp-coolers/b/ref=dp_bc_5?ie=UTF8&node=14335431" target="_blank" rel="nofollow"><strong>shop cooler deals at Amazon</strong></a></li><li><strong>Coleman Classic Playmate Coolers: </strong><a href="https://www.amazon.com/Igloo-7362-Playmate-Personal-12-Inch/dp/B000695WL2" target="_blank" rel="nofollow"><strong>was $33 now $20</strong></a></li><li><strong>Igloo Marine Cooler 25qt: </strong><a href="https://www.amazon.com/Igloo-Latitude-Marine-Ultra-Cooler/dp/B0BRL9S8L9" target="_blank" rel="nofollow"><strong>was $41 now $34</strong></a></li><li><strong>Coleman Classic Series Cooler 70qt: </strong><a href="https://www.amazon.com/Coleman-Insulated-Portable-Leak-Proof-Capacity/dp/B07X8RGTHK" target="_blank" rel="nofollow"><strong>was $84 now $74</strong></a></li><li><strong>Stanley All Day Madeleine Backpack Soft Cooler: </strong><a href="https://www.amazon.com/Madeleine-Midi-Cooler-Backpack-14-8/dp/B0CZ5MG67B?" target="_blank" rel="nofollow"><strong>was $170 now $124</strong></a></li><li><strong>Yeti Hopper M15 Portable Soft Cooler: </strong><a href="https://www.amazon.com/YETI-Hopper-Portable-Cooler-MagShield/dp/B0D6X5P13V" target="_blank" rel="nofollow"><strong>was $300 now $199</strong></a></li><li><strong>Ninja FrostVault 30QT: </strong><a href="https://www.amazon.com/dp/B0DPNCDYCZ?" target="_blank" rel="nofollow"><strong>was $279 now $229</strong></a></li></ul><h3 class="article-body__section" id="section-the-best-cooler-deals"><span>The Best Cooler Deals</span></h3><div class="product"><a data-dimension112="5c25a08e-a61b-4050-97b7-a9f2e9fe2335" data-action="Deal Block" data-label="Perfect for lunch, picnics and work days, the Playmate cooler is ready to join you on your next adventure. It's designed to be very easy to hold and carry, and the push-button lid will help prevent spills. This 7qt cooler can fit 9 cans." data-dimension48="Perfect for lunch, picnics and work days, the Playmate cooler is ready to join you on your next adventure. It's designed to be very easy to hold and carry, and the push-button lid will help prevent spills. This 7qt cooler can fit 9 cans." data-dimension25="$20" href="https://www.amazon.com/Igloo-7362-Playmate-Personal-12-Inch/dp/B000695WL2" 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:96.13%;"><img id="RCnEs4JhY7VGfMrrMS2ZE" name="Classic Playmate Coolers" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/RCnEs4JhY7VGfMrrMS2ZE.jpg" mos="" align="middle" fullscreen="" width="1500" height="1442" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Perfect for lunch, picnics and work days, the Playmate cooler is ready to join you on your next adventure. It's designed to be very easy to hold and carry, and the push-button lid will help prevent spills. This 7qt cooler can fit 9 cans.<a class="view-deal button" href="https://www.amazon.com/Igloo-7362-Playmate-Personal-12-Inch/dp/B000695WL2" target="_blank" rel="nofollow" data-dimension112="5c25a08e-a61b-4050-97b7-a9f2e9fe2335" data-action="Deal Block" data-label="Perfect for lunch, picnics and work days, the Playmate cooler is ready to join you on your next adventure. It's designed to be very easy to hold and carry, and the push-button lid will help prevent spills. This 7qt cooler can fit 9 cans." data-dimension48="Perfect for lunch, picnics and work days, the Playmate cooler is ready to join you on your next adventure. It's designed to be very easy to hold and carry, and the push-button lid will help prevent spills. This 7qt cooler can fit 9 cans." data-dimension25="$20">View Deal</a></p></div><div class="product"><a data-dimension112="002e545a-74cc-4cea-a845-3c27e3db8ace" data-action="Deal Block" data-label="This portable cooler is ideal for toting along food and beverages on your daily hiking excursions, trips to the beach or just keeping your drinks cold this summer. It will retain ice for 12 hours and you don't need to worry about spills thanks to its leakproof design. The cooler also features an adjustable padded sling strap and padded side handles for easy transporting to the top of the mountain." data-dimension48="This portable cooler is ideal for toting along food and beverages on your daily hiking excursions, trips to the beach or just keeping your drinks cold this summer. It will retain ice for 12 hours and you don't need to worry about spills thanks to its leakproof design. The cooler also features an adjustable padded sling strap and padded side handles for easy transporting to the top of the mountain." data-dimension25="$24" href="https://www.amazon.com/Coleman-Chiller-Soft-Cooler-Ocean/dp/B09HN1WTY5?" 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:84.68%;"><img id="Mcx2Aj6u6FJ6mYMpxEcdBJ" name="Coleman Cooler.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Mcx2Aj6u6FJ6mYMpxEcdBJ.jpg" mos="" align="middle" fullscreen="" width="679" height="575" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This portable cooler is ideal for toting along food and beverages on your daily hiking excursions, trips to the beach or just keeping your drinks cold this summer. It will retain ice for 12 hours and you don't need to worry about spills thanks to its leakproof design. The cooler also features an adjustable padded sling strap and padded side handles for easy transporting to the top of the mountain.  <a class="view-deal button" href="https://www.amazon.com/Coleman-Chiller-Soft-Cooler-Ocean/dp/B09HN1WTY5?" target="_blank" rel="nofollow" data-dimension112="002e545a-74cc-4cea-a845-3c27e3db8ace" data-action="Deal Block" data-label="This portable cooler is ideal for toting along food and beverages on your daily hiking excursions, trips to the beach or just keeping your drinks cold this summer. It will retain ice for 12 hours and you don't need to worry about spills thanks to its leakproof design. The cooler also features an adjustable padded sling strap and padded side handles for easy transporting to the top of the mountain." data-dimension48="This portable cooler is ideal for toting along food and beverages on your daily hiking excursions, trips to the beach or just keeping your drinks cold this summer. It will retain ice for 12 hours and you don't need to worry about spills thanks to its leakproof design. The cooler also features an adjustable padded sling strap and padded side handles for easy transporting to the top of the mountain." data-dimension25="$24">View Deal</a></p></div><div class="product"><a data-dimension112="6ae7f939-c719-4c4d-836b-23ae0d1643e6" data-action="Deal Block" data-label="The perfect compact cooler, now with 33% off. This Igloo cooler has a 25qt capacity, space for 36 cans. The non-slip handles make it easy to carry, and there's even a fish ruler on top to measure your catch." data-dimension48="The perfect compact cooler, now with 33% off. This Igloo cooler has a 25qt capacity, space for 36 cans. The non-slip handles make it easy to carry, and there's even a fish ruler on top to measure your catch." data-dimension25="$34" href="https://www.amazon.com/Igloo-Latitude-Marine-Ultra-Cooler/dp/B0BRL9S8L9" 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:66.93%;"><img id="KCGTcWXbH8iR4k2Csb4b5j" name="Marine Cooler 25QT" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/KCGTcWXbH8iR4k2Csb4b5j.jpg" mos="" align="middle" fullscreen="" width="1500" height="1004" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The perfect compact cooler, now with 33% off. This Igloo cooler has a 25qt capacity, space for 36 cans. The non-slip handles make it easy to carry, and there's even a fish ruler on top to measure your catch.<a class="view-deal button" href="https://www.amazon.com/Igloo-Latitude-Marine-Ultra-Cooler/dp/B0BRL9S8L9" target="_blank" rel="nofollow" data-dimension112="6ae7f939-c719-4c4d-836b-23ae0d1643e6" data-action="Deal Block" data-label="The perfect compact cooler, now with 33% off. This Igloo cooler has a 25qt capacity, space for 36 cans. The non-slip handles make it easy to carry, and there's even a fish ruler on top to measure your catch." data-dimension48="The perfect compact cooler, now with 33% off. This Igloo cooler has a 25qt capacity, space for 36 cans. The non-slip handles make it easy to carry, and there's even a fish ruler on top to measure your catch." data-dimension25="$34">View Deal</a></p></div><div class="product"><a data-dimension112="1862e48d-e856-4e56-a357-0039d42d8d63" data-action="Deal Block" data-label="Coleman Classic Cooler" data-dimension48="Coleman Classic Cooler" data-dimension25="$74" href="https://www.amazon.com/Coleman-Insulated-Portable-Leak-Proof-Capacity/dp/B07X8RGTHK" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:695px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="LLC8U9mUmqjs4623esUzGJ" name="Colemand Classic Cooler 70-quart" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/LLC8U9mUmqjs4623esUzGJ.png" mos="" align="middle" fullscreen="" width="695" height="695" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/home/coleman-classic-cooler-review" data-dimension112="1862e48d-e856-4e56-a357-0039d42d8d63" data-action="Deal Block" data-label="Coleman Classic Cooler" data-dimension48="Coleman Classic Cooler" data-dimension25="$74">Coleman Classic Cooler</a> isn't as fancy as some other brands, but this 70-quart version offers excellent capacity at a budget price. And that's why we recommend it as the best budget cooler in our buying guide. It can hold 100 cans, and doubles up as a seat — and if you're looking for a cooler to keep your catch on ice, it even has a fish ruler. It doesn't come with wheels, but with a discount we don't think you can ask for more.<a class="view-deal button" href="https://www.amazon.com/Coleman-Insulated-Portable-Leak-Proof-Capacity/dp/B07X8RGTHK" target="_blank" rel="nofollow" data-dimension112="1862e48d-e856-4e56-a357-0039d42d8d63" data-action="Deal Block" data-label="Coleman Classic Cooler" data-dimension48="Coleman Classic Cooler" data-dimension25="$74">View Deal</a></p></div><div class="product"><a data-dimension112="5eca3818-96fb-408d-8883-41ab35c5e284" data-action="Deal Block" data-label="Need a new cooler for spring roadtrips, barbecues or beach days? This Stanley cooler bag is the perfect pick. It's currently only $69 and comes in a variety of vibrant colors that are perfect for spring and summer. It fits up to 10 cans and will keep your food and drinks cold for hours." data-dimension48="Need a new cooler for spring roadtrips, barbecues or beach days? This Stanley cooler bag is the perfect pick. It's currently only $69 and comes in a variety of vibrant colors that are perfect for spring and summer. It fits up to 10 cans and will keep your food and drinks cold for hours." data-dimension25="$69" href="https://www.amazon.com/dp/B0CZ5SZ8B8?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1188px;"><p class="vanilla-image-block" style="padding-top:126.26%;"><img id="4oXDdUkUiWCGsfDz5dB8z7" name="Stanley cooler" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4oXDdUkUiWCGsfDz5dB8z7.jpg" mos="" align="middle" fullscreen="" width="1188" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Need a new cooler for spring roadtrips, barbecues or beach days? This Stanley cooler bag is the perfect pick. It's currently only $69 and comes in a variety of vibrant colors that are perfect for spring and summer. It fits up to 10 cans and will keep your food and drinks cold for hours. <a class="view-deal button" href="https://www.amazon.com/dp/B0CZ5SZ8B8?" target="_blank" rel="nofollow" data-dimension112="5eca3818-96fb-408d-8883-41ab35c5e284" data-action="Deal Block" data-label="Need a new cooler for spring roadtrips, barbecues or beach days? This Stanley cooler bag is the perfect pick. It's currently only $69 and comes in a variety of vibrant colors that are perfect for spring and summer. It fits up to 10 cans and will keep your food and drinks cold for hours." data-dimension48="Need a new cooler for spring roadtrips, barbecues or beach days? This Stanley cooler bag is the perfect pick. It's currently only $69 and comes in a variety of vibrant colors that are perfect for spring and summer. It fits up to 10 cans and will keep your food and drinks cold for hours." data-dimension25="$69">View Deal</a></p></div><div class="product"><a data-dimension112="c5aaf163-7f63-42d6-a182-f35a25515875" data-action="Deal Block" data-label="Possibly the best-looking backpack cooler out there — the Stanley All Day Madeleine Backpack Soft Cooler is 27% off right now. There's a large main pouch with space for 20 cans, and a compartment to stow small valuables too." data-dimension48="Possibly the best-looking backpack cooler out there — the Stanley All Day Madeleine Backpack Soft Cooler is 27% off right now. There's a large main pouch with space for 20 cans, and a compartment to stow small valuables too." data-dimension25="$124" href="https://www.amazon.com/dp/B0CZ5MG67B?" 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:93.20%;"><img id="m744BLnQrFWbVrCwhn2Pbc" name="All Day Madeleine Backpack Soft Cooler" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/m744BLnQrFWbVrCwhn2Pbc.jpg" mos="" align="middle" fullscreen="" width="1500" height="1398" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Possibly the best-looking backpack cooler out there — the Stanley All Day Madeleine Backpack Soft Cooler is 27% off right now. There's a large main pouch with space for 20 cans, and a compartment to stow small valuables too.<a class="view-deal button" href="https://www.amazon.com/dp/B0CZ5MG67B?" target="_blank" rel="nofollow" data-dimension112="c5aaf163-7f63-42d6-a182-f35a25515875" data-action="Deal Block" data-label="Possibly the best-looking backpack cooler out there — the Stanley All Day Madeleine Backpack Soft Cooler is 27% off right now. There's a large main pouch with space for 20 cans, and a compartment to stow small valuables too." data-dimension48="Possibly the best-looking backpack cooler out there — the Stanley All Day Madeleine Backpack Soft Cooler is 27% off right now. There's a large main pouch with space for 20 cans, and a compartment to stow small valuables too." data-dimension25="$124">View Deal</a></p></div><div class="product"><a data-dimension112="eeb71cd4-78fd-4019-9f9a-de03201560d1" data-action="Deal Block" data-label="Perfect for your next roadtrip, family picnic or sporting event, this 120 qt. cooler features dual snap-fit latches to keep contents secure during transportation. It's also super easy to carry thanks to its swing-up handles. It even features a drain plug for easy draining. The cooler will retain ice for up to 5 days." data-dimension48="Perfect for your next roadtrip, family picnic or sporting event, this 120 qt. cooler features dual snap-fit latches to keep contents secure during transportation. It's also super easy to carry thanks to its swing-up handles. It even features a drain plug for easy draining. The cooler will retain ice for up to 5 days." data-dimension25="$110" href="https://www.amazon.com/Igloo-Polar-Cooler-120-Quart-White/dp/B004QILD6W" 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:73.78%;"><img id="jFjwwiBUq7xKUtoEtNoUw3" name="Igloo coolers" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jFjwwiBUq7xKUtoEtNoUw3.jpg" mos="" align="middle" fullscreen="" width="679" height="501" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Perfect for your next roadtrip, family picnic or sporting event, this 120 qt. cooler features dual snap-fit latches to keep contents secure during transportation. It's also super easy to carry thanks to its swing-up handles. It even features a drain plug for easy draining. The cooler will retain ice for up to 5 days. <a class="view-deal button" href="https://www.amazon.com/Igloo-Polar-Cooler-120-Quart-White/dp/B004QILD6W" target="_blank" rel="nofollow" data-dimension112="eeb71cd4-78fd-4019-9f9a-de03201560d1" data-action="Deal Block" data-label="Perfect for your next roadtrip, family picnic or sporting event, this 120 qt. cooler features dual snap-fit latches to keep contents secure during transportation. It's also super easy to carry thanks to its swing-up handles. It even features a drain plug for easy draining. The cooler will retain ice for up to 5 days." data-dimension48="Perfect for your next roadtrip, family picnic or sporting event, this 120 qt. cooler features dual snap-fit latches to keep contents secure during transportation. It's also super easy to carry thanks to its swing-up handles. It even features a drain plug for easy draining. The cooler will retain ice for up to 5 days." data-dimension25="$110">View Deal</a></p></div><div class="product"><a data-dimension112="222477e3-49d0-45ff-91f7-8322cebd5eb1" data-action="Deal Block" data-label="This Igloo Iceless cooler is an ideal addition to any camping trip or road trip when you don't want to deal with ice. All you have to do is plug it into any 12V DC receptacle (cigarette lighter) and you'll be able to keep all your favorite drinks and snacks cold." data-dimension48="This Igloo Iceless cooler is an ideal addition to any camping trip or road trip when you don't want to deal with ice. All you have to do is plug it into any 12V DC receptacle (cigarette lighter) and you'll be able to keep all your favorite drinks and snacks cold." data-dimension25="$159" href="https://www.amazon.com/dp/B09SGS3RG8?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:732px;"><p class="vanilla-image-block" style="padding-top:84.84%;"><img id="kK3vAq5vxXxXPCrbDg8pKQ" name="Igloo Cooler" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kK3vAq5vxXxXPCrbDg8pKQ.jpg" mos="" align="middle" fullscreen="" width="732" height="621" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This Igloo Iceless cooler is an ideal addition to any camping trip or road trip when you don't want to deal with ice. All you have to do is plug it into any 12V DC receptacle (cigarette lighter) and you'll be able to keep all your favorite drinks and snacks cold.<a class="view-deal button" href="https://www.amazon.com/dp/B09SGS3RG8?" target="_blank" rel="nofollow" data-dimension112="222477e3-49d0-45ff-91f7-8322cebd5eb1" data-action="Deal Block" data-label="This Igloo Iceless cooler is an ideal addition to any camping trip or road trip when you don't want to deal with ice. All you have to do is plug it into any 12V DC receptacle (cigarette lighter) and you'll be able to keep all your favorite drinks and snacks cold." data-dimension48="This Igloo Iceless cooler is an ideal addition to any camping trip or road trip when you don't want to deal with ice. All you have to do is plug it into any 12V DC receptacle (cigarette lighter) and you'll be able to keep all your favorite drinks and snacks cold." data-dimension25="$159">View Deal</a></p></div><div class="product"><a data-dimension112="3ad66ec5-b218-4900-b4f5-79dedaec5f08" data-action="Deal Block" data-label="Perfect for cramped apartments and packed car boots, Coleman's new Snap ‘N Go offers hard-cooler insulation that collapses to one-third its size for effortless storage." data-dimension48="Perfect for cramped apartments and packed car boots, Coleman's new Snap ‘N Go offers hard-cooler insulation that collapses to one-third its size for effortless storage." data-dimension25="$153" href="https://www.amazon.com/Coleman-Collapsible-Hard-Sided-Waterproof-Space-Saving/dp/B0GF2WVDJK" 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="58sArE55E7EyeV8mY8Fq5U" name="Snap ‘N Go Collapsible Cooler (45Qt)" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/58sArE55E7EyeV8mY8Fq5U.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Perfect for cramped apartments and packed car boots, Coleman's new Snap ‘N Go offers hard-cooler insulation that collapses to one-third its size for effortless storage.<a class="view-deal button" href="https://www.amazon.com/Coleman-Collapsible-Hard-Sided-Waterproof-Space-Saving/dp/B0GF2WVDJK" target="_blank" rel="nofollow" data-dimension112="3ad66ec5-b218-4900-b4f5-79dedaec5f08" data-action="Deal Block" data-label="Perfect for cramped apartments and packed car boots, Coleman's new Snap ‘N Go offers hard-cooler insulation that collapses to one-third its size for effortless storage." data-dimension48="Perfect for cramped apartments and packed car boots, Coleman's new Snap ‘N Go offers hard-cooler insulation that collapses to one-third its size for effortless storage." data-dimension25="$153">View Deal</a></p></div><div class="product"><a data-dimension112="b535fe42-93ed-4170-ab54-7b6db548d56f" data-action="Deal Block" data-label="This soft-sided cooler is perfect for day trips, picnics or outdoor adventures, and it's not too heavy to carry around. Compact and portable, it holds up to 13 cans and features a leakproof zipper, making it a must-have for on-the-go convenience." data-dimension48="This soft-sided cooler is perfect for day trips, picnics or outdoor adventures, and it's not too heavy to carry around. Compact and portable, it holds up to 13 cans and features a leakproof zipper, making it a must-have for on-the-go convenience." data-dimension25="$200" href="https://www.amazon.com/dp/B0DS6MKB4Y?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1418px;"><p class="vanilla-image-block" style="padding-top:65.66%;"><img id="ezMMD2GC3rvaXXG9KUPRDA" name="yeti cooler" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ezMMD2GC3rvaXXG9KUPRDA.jpg" mos="" align="middle" fullscreen="" width="1418" height="931" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This soft-sided cooler is perfect for day trips, picnics or outdoor adventures, and it's not too heavy to carry around. Compact and portable, it holds up to 13 cans and features a leakproof zipper, making it a must-have for on-the-go convenience.<a class="view-deal button" href="https://www.amazon.com/dp/B0DS6MKB4Y?" target="_blank" rel="nofollow" data-dimension112="b535fe42-93ed-4170-ab54-7b6db548d56f" data-action="Deal Block" data-label="This soft-sided cooler is perfect for day trips, picnics or outdoor adventures, and it's not too heavy to carry around. Compact and portable, it holds up to 13 cans and features a leakproof zipper, making it a must-have for on-the-go convenience." data-dimension48="This soft-sided cooler is perfect for day trips, picnics or outdoor adventures, and it's not too heavy to carry around. Compact and portable, it holds up to 13 cans and features a leakproof zipper, making it a must-have for on-the-go convenience." data-dimension25="$200">View Deal</a></p></div><div class="product"><a data-dimension112="010c1c17-fc08-42c1-b5a3-d75f644424c5" data-action="Deal Block" data-label="best coolers" data-dimension48="best coolers" data-dimension25="$199" href="https://www.amazon.com/YETI-Hopper-Portable-Cooler-MagShield/dp/B0D6X5P13V?" 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:96.02%;"><img id="jpu7KN2YjGKKTB5L2MEpMQ" name="YETI hopper. jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jpu7KN2YjGKKTB5L2MEpMQ.jpg" mos="" align="middle" fullscreen="" width="679" height="652" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>One of the <a href="https://www.tomsguide.com/best-picks/best-coolers" data-dimension112="010c1c17-fc08-42c1-b5a3-d75f644424c5" data-action="Deal Block" data-label="best coolers" data-dimension48="best coolers" data-dimension25="$199">best coolers</a> we've tested, the YETI Hopper M Series is a fantastic cooler for keeping your stuff chilled, but that’s not all it does. The soft cooler stands out for its wide mouth and magnetized closing with buckles. This means it can withstand impacts without opening up.<a class="view-deal button" href="https://www.amazon.com/YETI-Hopper-Portable-Cooler-MagShield/dp/B0D6X5P13V?" target="_blank" rel="nofollow" data-dimension112="010c1c17-fc08-42c1-b5a3-d75f644424c5" data-action="Deal Block" data-label="best coolers" data-dimension48="best coolers" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="fb9ce79e-6a9c-447d-b8e9-23e88561b4fa" data-action="Deal Block" data-label="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." data-dimension48="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." 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:1486px;"><p class="vanilla-image-block" style="padding-top:100.94%;"><img id="iVdQfcQXuJqUK4B8uRHRN5" name="Ninja Frostvault" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/iVdQfcQXuJqUK4B8uRHRN5.jpg" mos="" align="middle" fullscreen="" width="1486" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>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="fb9ce79e-6a9c-447d-b8e9-23e88561b4fa" data-action="Deal Block" data-label="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." data-dimension48="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." data-dimension25="$229">View Deal</a></p></div><div class="product"><a data-dimension112="3eecfbc2-a966-4775-96be-5ad584e02ac4" data-action="Deal Block" data-label="If you love adventure almost as much as you love beer, this convenient and secure backpack cooler is a must-have. The YETI Hopper can carry up to 36 cans of your favorite beverage while keeping your hands free — and most importantly, your drinks ice cold." data-dimension48="If you love adventure almost as much as you love beer, this convenient and secure backpack cooler is a must-have. The YETI Hopper can carry up to 36 cans of your favorite beverage while keeping your hands free — and most importantly, your drinks ice cold." data-dimension25="$260" href="https://www.amazon.com/YETI-Hopper-Backpack-Cooler-MagShield/dp/B0DCW87YSY" 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:94.55%;"><img id="W2bVFcrWaEYWduVsWR9Dri" name="YETI cooler backpack" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/W2bVFcrWaEYWduVsWR9Dri.jpg" mos="" align="middle" fullscreen="" width="679" height="642" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you love adventure almost as much as you love beer, this convenient and secure backpack cooler is a must-have. The YETI Hopper can carry up to 36 cans of your favorite beverage while keeping your hands free — and most importantly, your drinks ice cold.<a class="view-deal button" href="https://www.amazon.com/YETI-Hopper-Backpack-Cooler-MagShield/dp/B0DCW87YSY" target="_blank" rel="nofollow" data-dimension112="3eecfbc2-a966-4775-96be-5ad584e02ac4" data-action="Deal Block" data-label="If you love adventure almost as much as you love beer, this convenient and secure backpack cooler is a must-have. The YETI Hopper can carry up to 36 cans of your favorite beverage while keeping your hands free — and most importantly, your drinks ice cold." data-dimension48="If you love adventure almost as much as you love beer, this convenient and secure backpack cooler is a must-have. The YETI Hopper can carry up to 36 cans of your favorite beverage while keeping your hands free — and most importantly, your drinks ice cold." data-dimension25="$260">View Deal</a></p></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Don't forget this crucial task before cutting your lawn — and protect your mower blades this summer ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/dont-forget-this-crucial-task-before-cutting-your-lawn-and-protect-your-mower-blades-this-summer</link>
                                                                            <description>
                            <![CDATA[ This is the one crucial lawn care task that you must do to protect your mower blades this summer. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">HoRrEVxzrBMb6UmNPjZHR5</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/MHcTkyeSz7f4tJ5baYFxF3-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 06 Jul 2026 11:46:36 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Lawn Care]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                                    <dc:creator><![CDATA[ Cynthia Lawrence ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/CCuSgQpd5NyZ46CgoF9cva.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/MHcTkyeSz7f4tJ5baYFxF3-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                        <media:description><![CDATA[&lt;p&gt;Cleaning lawnmower blades with brush&lt;/p&gt;]]></media:description>                                                            <media:text><![CDATA[Cleaning lawnmower blades with brush]]></media:text>
                                <media:title type="plain"><![CDATA[Cleaning lawnmower blades with brush]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/MHcTkyeSz7f4tJ5baYFxF3-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As the weather warms up, there's nothing more satisfying than having a lush, green lawn to enjoy — particularly when preparing for outdoor entertaining.</p><p>And while you might already have a <a href="https://www.tomsguide.com/features/the-ultimate-lawn-care-guide-essential-tips-and-tricks-for-getting-the-perfect-lawn">lawn care routine</a> that you stick to, you might be forgetting to do this one crucial step before cutting your grass. In fact, it’s a common mistake we often make when mowing our lawn, but if left unchecked, it can cause long-term damage to our mower’s blades. </p><p>What’s more, this could ruin your lush, healthy grass just as the summer begins. So if you want to ensure a greener lawn, don’t skip this one lawn mower check. </p><h3 class="article-body__section" id="section-make-sure-the-grass-is-dry-before-cutting"><span>Make sure the grass is dry before cutting</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:4792px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="xQmBW7pwjvJhczbjCuPihS" name="Main.jpg" alt="A hand touching the grass in the yard" src="https://cdn.mos.cms.futurecdn.net/xQmBW7pwjvJhczbjCuPihS.jpg" mos="" align="middle" fullscreen="" width="4792" height="2696" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">A hand touching the grass in the yard </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>While this might not seem necessary in warmer months, you should always check that your grass is dry before getting the mower out. Be it from morning dew or a summer shower, excess moisture can make all the difference to your mower blades.</p><p>This is due to the fact that damp blades of grass tend to stick or clump together, which can significantly blunt your mower's cutting edge over time. Consequently, this makes the blades work less effectively and could cause them to overheat, as they’ll need to work harder to get through the clumps — and potentially make your mower break down.</p><p>In addition, moist cuttings can build up beneath the mower deck and across your blades, which results in rusted metal components and a decline in your machine’s efficiency.</p><p>Of course, keeping your lawn mower in top condition is vital, so ensure your mower blades are sharp and avoid mowing with blunt equipment. </p><p>There are many blade sharpening tools available online; however, a metal file also works well — always follow the safety guidelines for your mower. Check out our guide on <a href="https://www.tomsguide.com/how-to/how-to-sharpen-lawn-mower-blades-in-9-simple-steps">how to sharpen lawn mower blades</a> for more info.  </p><h3 class="article-body__section" id="section-uneven-and-lackluster-grass"><span>Uneven and lackluster grass</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:3881px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Z6e7zssSp9CSJ5vWZnm3Kb" name="shutterstock_1311895748.jpg" alt="Lawnmower cutting through wet grass" src="https://cdn.mos.cms.futurecdn.net/Z6e7zssSp9CSJ5vWZnm3Kb.jpg" mos="" align="middle" fullscreen="" width="3881" height="2183" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Lawnmower cutting through wet grass </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>To make matters worse, mowing when the grass is moist can trigger various other problems that can affect your turf's health. The first is torn or unevenly cut grass, as the sheer weight of moisture forces individual grass stalks to droop, preventing them from remaining upright and resulting in an uneven, ragged finish across your turf.</p><p>Another issue is the breeding of fungus and other diseases, as they thrive in the damp conditions of these rough cuts — causing damage to soil.  </p><p>It’s worth noting that while cutting lightly damp grass on a single occasion probably won’t lead to long-term problems, it’s still best to avoid it as much as possible. </p><p>Just be sure to avoid these <a href="https://www.tomsguide.com/how-to/7-lawnmower-mistakes-you-are-probably-making-right-now">7 lawnmower mistakes you’re probably making right now.</a></p><h3 class="article-body__section" id="section-when-is-the-best-time-to-mow-your-lawn"><span>When is the best time to mow your lawn?</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:5324px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kiAt9GaW4dFDBHE86DBtvD" name="shutterstock_2114204516 (1).jpg" alt="A lawn mower cutting the grass" src="https://cdn.mos.cms.futurecdn.net/kiAt9GaW4dFDBHE86DBtvD.jpg" mos="" align="middle" fullscreen="" width="5324" height="2995" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">A lawn mower cutting the grass </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>Gardening experts suggest that the ideal time to mow is mid-morning (by 10 am), as your lawn has had sufficient time to dry. In addition, allowing your turf enough time to dry and mowing by this time ensures your lawn has the full day to heal from the cut.</p><p>Alternatively, if you can’t get round to mid-morning, late afternoon (between 4-6 pm) is the second-best time of day to mow your lawn. This is when the sun is not at its peak, and there is no risk of the heat scorching the lawn. Also, mowing at this hour additionally provides your grass with ample time to heal before nightfall. </p><p>For more top tips on getting a greener lawn, check out these <a href="https://www.tomsguide.com/home/lawn-care/11-secrets-to-a-lawn-thats-greener-than-your-neighbors">11 secrets to a lawn that’s greener than your neighbor's.</a></p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ODDR8O"></div>                            </div>                            <script src="https://kwizly.com/embed/ODDR8O.js" async></script><h3 class="article-body__section" id="section-lawncare-essentials-we-love"><span>Lawncare essentials we love</span></h3>        <div class="featured_product_block featured_block_hero" data-id="3388c929-4a16-416b-a3d5-22383add1d14">            <a href="https://www.amazon.com/Scotts-17508/dp/B0168K936W/ref=sr_1_1_sspa?" data-model-name="Scotts Ez Seed Patch & Repair for Sun and Shade" 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/ZRWks9aKfGEVKLwPaLqmhh.jpg" alt="Scotts Ez Seed Patch & Repair for Sun and Shade - Combination Mulch, Sun and Shade Grass Seed & Fertilizer for Bare Spots & Lawn Patch Repair, Covers Up to 85 Sq. Ft., 3.75 Lb."></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon</div>                                        <div class="featured__title">Scotts Ez Seed Patch & Repair for Sun and Shade</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="e8c2084b-a16d-494d-92f4-018af384a62d">            <a href="https://www.amazon.com/Garden-Guru-Handheld-Spreader-Shaker/dp/B0F8P8QM3V/ref=sr_1_8?" data-model-name="Garden Guru Handheld Seed Spreader Shaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:82.80%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ezrwVY6BYsgBZtBcNhMTm9.jpg" alt="Garden Guru Handheld Seed Spreader Shaker – Multi Use Grass Seed, Fertilizer, Salt, Ice & Snow Melt Spreader – Adjustable Twist Cap W Multiple Size Openings – Lightweight 1l Capacity Hand Seeder"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon</div>                                        <div class="featured__title">Garden Guru Handheld Seed Spreader Shaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="3b837df4-d661-4794-8a5a-5356e9937853">            <a href="https://www.amazon.com/Fiskars-339950-1002-Puller-Gardening-Removal/dp/B0030MIHAU?" data-model-name="Fiskars Stand-Up Weed Puller Tool" 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/68PWsuXsMu9y5iotDQ8BaZ.jpg" alt="Fiskars Stand-Up Weed Puller Tool, 4 Serrated Stainless Steel Claws Grip Roots for Permanent Removal, Reinforced Foot Platform Penetrates Tough Soil, Easy-Eject & Ergonomic Offset Handle, 39""></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon</div>                                        <div class="featured__title">Fiskars Stand-Up Weed Puller Tool</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ddc778c0-9bef-49c6-bad3-713d3f09a172">            <a href="https://www.amazon.com/Greenworks-G-MAX-Mower-Batteries-Charger/dp/B086K59Q79/ref=sr_1_3?" data-model-name="Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower" 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/8LF2UvRs8EXvmebaZefwzA.jpg" alt="Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower (200+ Compatible Tools), (2) 4.0ah Batteries and Dual Port Rapid Charger Included"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon</div>                                        <div class="featured__title">Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower</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/home/gardening/this-is-the-reason-why-your-lawn-mower-grass-cuttings-are-clumping-and-the-simple-fix-to-solve-it">This is the reason why your lawn mower grass cuttings are clumping — and the simple fix to solve it</a></li><li><a href="https://www.tomsguide.com/home/outdoors/how-to-use-coffee-grounds-as-lawn-fertilizer-5-tips-for-a-lush-lawn">How to use coffee grounds as lawn fertilizer — 5 tips for a lush lawn</a></li><li><a href="https://www.tomsguide.com/home/outdoors/the-secret-to-a-lush-green-lawn-is-this-usd3-bathroom-item-and-you-might-already-own-it">The secret to a lush green lawn is this $3 bathroom item — and you might already own it</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ How to mow a checkerboard pattern in your lawn this summer — and impress the neighbors ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/how-to-mow-a-checkerboard-pattern-in-your-lawn-this-summer-and-impress-the-neighbors</link>
                                                                            <description>
                            <![CDATA[ Gardening expert reveals how to do checkerboard patterns in your lawn to spruce up your backyard this summer. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">K4UVv4MonWg6YbjgWFzcT9</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/K2AkhqzJATyirrHcmaS6ea-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 04 Jul 2026 06:45:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                                    <dc:creator><![CDATA[ Cynthia Lawrence ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/CCuSgQpd5NyZ46CgoF9cva.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/K2AkhqzJATyirrHcmaS6ea-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Man mowing lawn]]></media:description>                                                            <media:text><![CDATA[Man mowing lawn]]></media:text>
                                <media:title type="plain"><![CDATA[Man mowing lawn]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/K2AkhqzJATyirrHcmaS6ea-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Summer is here, and the perfect time to get out and enjoy your outdoor spaces in style. Whether you take pride in your<a href="https://www.tomsguide.com/home/gardening/i-asked-gardening-experts-the-secret-to-a-greener-lawn-heres-what-they-want-you-to-do-this-spring"> lush lawn</a> or are preparing for hosting gatherings, a patterned lawn can truly provide that wow factor and elevate your entire garden.</p><p>But if you don’t want a classic <a href="https://www.tomsguide.com/how-to/how-to-stripe-your-lawn">striped lawn</a> or even a diamond pattern, why not try a twist with the checkerboard effect? Known for its unique, geometric squares, you can easily replicate this look by switching your travel path while mowing.</p><p>In fact, this specific method is a<a href="https://www.tomsguide.com/wellness/fitness/i-tried-the-new-ghin-apps-massive-upgrade-that-will-settle-every-golf-course-argument"> favorite for golf course managers</a>, which can instantly give your own backyard a premium appeal. What’s more, this method will make your grass appear healthier.</p><p>While it may seem like a job for the professionals, mastering the checkerboard effect is actually quite easy to do. I asked a grass expert to share their top tips on how to mow a checkerboard pattern the right way — so your lawn can be the envy of your neighbors. </p><h2 id="how-to-create-a-checkerboard-lawn-in-2-easy-steps">How to create a checkerboard lawn — in 2 easy steps </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:5749px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LpuSM9DGxwcEeQH9veGXnC" name="shutterstock_editorial_14803221e" alt="Lawnmower cutting stripes in one direction" src="https://cdn.mos.cms.futurecdn.net/LpuSM9DGxwcEeQH9veGXnC.jpg" mos="" align="middle" fullscreen="" width="5749" height="3234" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Lawnmower cutting stripes in one direction </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>Creating a checkerboard pattern is pretty straightforward. In this method, you simply need to alternate your mowing path between horizontal and vertical passes across your turf. </p><p>Before you start, experts advise that your grass should be thick and healthy. This is because the pattern actually comes from the blades leaning in different directions and the light bouncing off it. So if your lawn is patchy, bumpy, or in bad condition overall, this will affect the overall results. </p><div><blockquote><p>Alternate your mowing path between horizontal and vertical passes across your turf. </p></blockquote></div><p>“You need to make sure you have a healthy and even lawn before you begin,” advises Jonathan Davis, grass expert at <a href="https://74n5c4m7.r.eu-west-1.awstrack.me/L0/https:%2F%2Fwww.lawnsmith.co.uk%2F%3Fsrsltid=AfmBOooIkJWgMxC-1t6eQmXY5XvbqXKqmXATVwwOvAj16jq57CAERa59/1/0102019f17a2cc5e-f8deace1-ec76-4ed8-9564-ca700f7c5bbb-000000/ZJoQ8msOlwnzzHCnXOkJvz6_bqI=473" target="_blank">Lawnsmith</a>.  </p><p>“The checkerboard appearance comes from the blades leaning in different directions and the light bouncing off it, so a bumpy or patchy lawn might not be as effective”.</p><p>In addition, it’s ideal to have helpful tools such as a lawn roller; however, most standard mowers have this already attached. More importantly, you should ensure that your mower's blades are perfectly sharp before you begin. </p><p>“A roller is essential; most standard mowers have these attached to the rear, but you can use a separate roller if you prefer, ”advises Davis. “Plot your path, either using string or stakes. You can try to mentally plan your route, but this could lead to wonky lines. “</p><p>Begin by mowing a horizontal line through your turf. Then, rotate your mower to complete a vertical pass alongside it, alternating your route with every subsequent pass. </p><p>“Mow the first set of stripes in one direction across the whole lawn, alternating the direction of travel on each pass - up and then back down,” adds Davis.</p><p>“Then turn 90 degrees and repeat the process. The second pass will intersect the first, and where the two directions overlap, you get the checkerboard squares instead of simple stripes.”</p><div><blockquote><p>Mow the first set of stripes in one direction across the whole lawn, alternating the direction of travel on each pass. Then turn 90 degrees and repeat the process</p><p>Jonathan Davis, grass expert </p></blockquote></div><p>By following these steps precisely, you should achieve a checkerboard pattern, although bear in mind the lines may be a little subtle at first. In any case, as your grass grows, you’ll need to re-mow or re-roll over the original pattern to ensure your distinct pattern. </p><p>“Changing the pattern of your lawn can have some benefits,” states Davis. “It prevents the grass from leaning in one set direction, which can weaken it over time and make it grow unevenly. </p><p>“It can also lead to less soil compaction. Driving or walking over the same sections in the same way can compress the soil, which restricts root growth and drainage. Switching your route up spreads that wear out.”</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:5533px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="hfrtB6rrich6rWrsYBS8Xh" name="shutterstock_674227348" alt="Chess pieces on checkerboard lawn" src="https://cdn.mos.cms.futurecdn.net/hfrtB6rrich6rWrsYBS8Xh.jpg" mos="" align="middle" fullscreen="" width="5533" height="3112" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Chess pieces on checkerboard lawn </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>The same rules apply for regular mowing, and you must avoid doing a checkerboard pattern when the grass is damp or moist. This can lead to clumping — or ruin the overall aesthetic. </p><p>“Avoid doing it when the ground is wet as this can lead to clumping. Mowing during periods of hot weather can also lead to your turf becoming stressed,” Davis explains.</p><p>“A striped, two-tone checkerboard effect you see on sports fields or golf courses is actually quite achievable in your own garden”.</p><p>So if you’re looking to truly spruce up your backyard this season, give your turf a premium checkerboard upgrade.</p><p>For more top lawncare tips, check out <a href="https://www.tomsguide.com/home/lawn-care/11-secrets-to-a-lawn-thats-greener-than-your-neighbors">11 secrets to a lawn that’s greener than your neighbor's.</a></p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eG0N2W"></div>                            </div>                            <script src="https://kwizly.com/embed/eG0N2W.js" async></script><h3 class="article-body__section" id="section-lawncare-essentials-we-love"><span>Lawncare essentials we love</span></h3>        <div class="featured_product_block featured_block_hero" data-id="908cae43-55fa-4da3-8574-24b29266d738">            <a href="https://www.amazon.com/Garden-Guru-Handheld-Spreader-Shaker/dp/B0F8P8QM3V/ref=sr_1_8?" data-model-name="Garden Guru Handheld Seed Spreader Shaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:82.80%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ezrwVY6BYsgBZtBcNhMTm9.jpg" alt="Garden Guru Handheld Seed Spreader Shaker – Multi Use Grass Seed, Fertilizer, Salt, Ice & Snow Melt Spreader – Adjustable Twist Cap W Multiple Size Openings – Lightweight 1l Capacity Hand Seeder"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon</div>                                        <div class="featured__title">Garden Guru Handheld Seed Spreader Shaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="f740eec9-fe02-43b7-b239-5b64bced9d51">            <a href="https://www.amazon.com/dp/B07BD2GZW8" data-model-name="Garden Gloves" 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/YyBJP4YUpLVNV4q2yKFfjS.jpg" alt="Amazing Stuff For You green and gray gardening gloves"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazing Stuff</div>                                        <div class="featured__title">Garden Gloves</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="36c3c5ee-d7ff-4592-9d90-3e70c1b065ba">            <a href="https://www.amazon.com/Grampas-Weeder-CW-01-Original-Remover/dp/B001D1FFZA?" data-model-name="Grampa's Weeder" 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/kP7pgu2tpxz8d34HtVQgxC.jpg" alt="Grampa's Weeder The Original Stand Up Weed Puller Tool with Long Handle"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Grampa's Weeder</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="e9b94b08-caad-456c-b104-34a12eed1cce">            <a href="https://www.amazon.com/Greenworks-G-MAX-Mower-Batteries-Charger/dp/B086K59Q79/ref=sr_1_3?" data-model-name="Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower" 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/8LF2UvRs8EXvmebaZefwzA.jpg" alt="Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower (200+ Compatible Tools), (2) 4.0ah Batteries and Dual Port Rapid Charger Included"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon</div>                                        <div class="featured__title">Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower</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/home/gardening/why-do-i-have-yellow-patches-in-my-lawn-gardening-experts-share-their-top-tips-on-how-to-fix-it">Why do I have yellow patches in my lawn? Gardening experts share their top tips on how to fix it</a></li><li>How<a href="https://www.tomsguide.com/how-to/how-to-dethatch-a-lawn"> to dethatch a lawn</a></li><li><a href="https://www.tomsguide.com/home/5-easy-tips-to-fix-bare-patches-on-your-lawn-this-spring">5 easy tips to fix bare patches on your lawn this spring</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Shark just launched a vacuum that can 'transform' into 3 different models — I can't decide which one I like the most ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/shark-just-launched-a-vacuum-that-can-transform-into-3-different-models-i-cant-decide-which-one-i-like-the-most</link>
                                                                            <description>
                            <![CDATA[ Shark has just launched another addition to its vacuum line-up, and not only does it have a very cool feature, but it's got a cool name to boot. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">Dam29vY9FFfExrJUk5pnrE</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/uD8c7gAJfHimxMedEMFs5m-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 02 Jul 2026 09:58:10 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></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/uD8c7gAJfHimxMedEMFs5m-1280-80.jpg">
                                                            <media:credit><![CDATA[SharkNinja]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Shark PowerDetect Transformer being used in a home]]></media:description>                                                            <media:text><![CDATA[The Shark PowerDetect Transformer being used in a home]]></media:text>
                                <media:title type="plain"><![CDATA[The Shark PowerDetect Transformer being used in a home]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/uD8c7gAJfHimxMedEMFs5m-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Shark is a brand that knows how to make a good vacuum. In fact, when it comes to the <a href="https://www.tomsguide.com/best-picks/best-vacuum-cleaners">best vacuum cleaners </a>we've ever tested, Shark holds two of the seven top spots. Impressive, huh? <em>But</em>, it doesn't coast on its past success, releasing new innovative products on a pretty regular basis.</p><p>Its latest release, though, really feels like it might have outdone itself. The Shark PowerDetect Transformer vacuum has just launched, but it's not just one product – it's <em>three</em>. While it's an upright vacuum at first glance, it can easily be reconfigured into a lightweight stick vacuum or powerful handheld. </p><p>Now, when I hear the name Transformer, my mind goes immediately to the iconic robots. And while I loved that as a kid, my adult form is a <em>big</em> fan of the vacuum version.</p><div class="product"><a data-dimension112="52f5fc12-6c53-4273-80cb-ba51261ec3f9" data-action="Deal Block" data-label="The Shark PowerDetect Transformer is a full-size upright vacuum capable of powerful deep cleans. But, it can also be easily converted into a lightweight stick for everyday cleaning or a handheld vacuum with a lot more reach. Available for $529.99, you can use the code 'TRIO' right now to secure $130 off." data-dimension48="The Shark PowerDetect Transformer is a full-size upright vacuum capable of powerful deep cleans. But, it can also be easily converted into a lightweight stick for everyday cleaning or a handheld vacuum with a lot more reach. Available for $529.99, you can use the code 'TRIO' right now to secure $130 off." data-dimension25="$529.99" href="https://www.sharkninja.com/shark-powerdetect-transformer-upright-vacuum/LX5000.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:250px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="QpqquGzSAcnhPMTPJjzkqf" name="shark_powerdetect_transformer_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/QpqquGzSAcnhPMTPJjzkqf.png" mos="" align="middle" fullscreen="" width="250" height="250" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Shark PowerDetect Transformer is a full-size upright vacuum capable of powerful deep cleans. But, it can also be easily converted into a lightweight stick for everyday cleaning or a handheld vacuum with a lot more reach. Available for $529.99, you can use the code 'TRIO' right now to secure $130 off.<a class="view-deal button" href="https://www.sharkninja.com/shark-powerdetect-transformer-upright-vacuum/LX5000.html" target="_blank" rel="nofollow" data-dimension112="52f5fc12-6c53-4273-80cb-ba51261ec3f9" data-action="Deal Block" data-label="The Shark PowerDetect Transformer is a full-size upright vacuum capable of powerful deep cleans. But, it can also be easily converted into a lightweight stick for everyday cleaning or a handheld vacuum with a lot more reach. Available for $529.99, you can use the code 'TRIO' right now to secure $130 off." data-dimension48="The Shark PowerDetect Transformer is a full-size upright vacuum capable of powerful deep cleans. But, it can also be easily converted into a lightweight stick for everyday cleaning or a handheld vacuum with a lot more reach. Available for $529.99, you can use the code 'TRIO' right now to secure $130 off." data-dimension25="$529.99">View Deal</a></p></div><h2 id="about-the-shark-powerdetect-transformer">About the Shark PowerDetect Transformer</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="QEkScHRbAQhpbx3JJvb2Dm" name="shark_powerdetect_transformer_1" alt="The Shark PowerDetect Transformer being used in a home" src="https://cdn.mos.cms.futurecdn.net/QEkScHRbAQhpbx3JJvb2Dm.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: SharkNinja)</span></figcaption></figure><p>The Shark PowerDetect Transformer is a little bit of everything. While you've got your full-size upright to begin with, it can easily be changed into a stick vacuum that's flexible and lightweight, or a handheld for those above-floor and tight spaces.</p><p>What you get with a lot of vacuums right now is a whole suite of cleaning tools to complete the job, but what the PowerDetect Transformer aims to do is streamline all of that into just one transforming system. And it's really as easy as one click to turn the upright into a handheld with the removable pod. Then, you simply click it back onto the upright and it'll empty itself into the main dust cup.</p><p>As Petra Oman, VP of Marketing at SharkNinja explains: "The upright vacuum has looked and worked the same way for decades. We saw an opportunity to rethink the category by eliminating the bulky hose and creating a system that adapts to the way people actually clean."</p><p>And it's not just an impressive reconfiguring vacuum that you get, but there's smarts packed inside, too. With both PowerDetect and Reveal technologies onboard, it's capable of finding hidden dirt and debris that usually goes unnoticed and then responds to the environment from floor types to edges.</p><h2 id="here-s-why-i-can-t-wait-to-try-it">Here's why I can't wait to try 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="vLB9pajbVMBZJpHhmUJyCm" name="shark_powerdetect_transformer_4" alt="The Shark PowerDetect Transformer being used in a home" src="https://cdn.mos.cms.futurecdn.net/vLB9pajbVMBZJpHhmUJyCm.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: SharkNinja)</span></figcaption></figure><p>I've got a Shark vacuum right now, but I'll admit that I get fed up with the hose getting in my way, especially when going up the stairs or reaching for those annoying corners. I also absolutely hate when the attachments have a mind of their own, detaching themselves from my upright when I'm manoeuvring around.</p><p>What I've really been after is something streamlined that's still capable of tackling my whole home, and that's where the Shark PowerDetect Transformer seems like it could really fit the bill. </p><p>All three cleaning forms in one system for one price? Sign me up. And while it's a little more expensive than the likes of the <a href="https://www.tomsguide.com/home/shark-cordless-powerdetect-clean-and-empty-vacuum-system-review">Shark PowerDetect Clean & Empty Vacuum,</a> which launched for $499, or the <a href="https://www.tomsguide.com/home/home-appliances/shark-stratos-az3002-upright-vacuum-review">Shark Stratos</a> for $399, it's also not <em>that</em> much more considering it's a 3-in-1.</p><p>I'm also a big fan of the colorways, as well as the big Transformer logo down the shaft of the handheld. I never thought I'd see a day when I'd get excited about a Transformer that was a vacuum instead of a robot, but here we are. I'll reserve my full judgment for when we get hands-on for a full review.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eGRoEX"></div>                            </div>                            <script src="https://kwizly.com/embed/eGRoEX.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/home/smart-home/i-just-tried-the-new-shark-powerdetect-robot-vacuum-and-this-one-upgrade-changes-everything">I just tried the new Shark PowerDetect robot vacuum — and this one upgrade changes everything</a></li><li><a href="https://www.tomsguide.com/home/which-shark-robot-vacuum-should-i-buy-how-to-pick-the-best-model-for-your-needs-and-budget">Which Shark robot vacuum should I buy? How to pick the best model for your needs and budget</a></li><li><a href="https://www.tomsguide.com/home/i-tried-sharks-new-misting-and-cooling-handheld-fan-but-is-the-chillpill-worth-the-hype">I tried Shark's new misting and cooling handheld fan, but is the ‘ChillPill’ worth the hype?</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tried this zero-waste gardening hack to help my tomato plants thrive — and all I needed was a common kitchen scrap ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/i-tried-this-zero-waste-gardening-hack-to-help-my-tomato-plants-thrive-and-all-i-needed-was-a-common-kitchen-scrap</link>
                                                                            <description>
                            <![CDATA[ I'm always on the hunt for garden hacks and it turns out there's one common kitchen scrap I should've been using to help my tomatoes grow. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">7hLQ9Yc9db6CTkPpSwu4K</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/AVAbuuHoEmq7gRCD4cJUTL-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 30 Jun 2026 11:59:18 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></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/AVAbuuHoEmq7gRCD4cJUTL-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Holding a homegrown tomato plant]]></media:description>                                                            <media:text><![CDATA[Holding a homegrown tomato plant]]></media:text>
                                <media:title type="plain"><![CDATA[Holding a homegrown tomato plant]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/AVAbuuHoEmq7gRCD4cJUTL-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I've been having trouble with my tomatoes. As part of my grow-it-yourself mission this year, I planted a variety of vegetables in spring that I was hoping I'd be harvesting by now for some delicious summer salads. Unfortunately, my garden has had other plans. Despite taking <a href="https://www.tomsguide.com/home/im-going-to-be-trying-these-top-4-tips-to-get-my-tomatoes-tasting-as-if-they-were-grown-in-italy-after-an-italian-farmer-shared-why-i-shouldnt-rush-the-land">top tips from an Italian farmer</a> and I've even adopting a <a href="https://www.tomsguide.com/home/gardening/why-are-my-tomato-plant-leaves-turning-yellow-5-steps-to-save-your-harvest-clone">3-step pruning system</a>, I'm still always on the lookout for more ways to help my tomatoes grow.</p><p>And I've recently stumbled upon a cheap and potentially genius gardening hack that could be my answer to high-yield and hopefully ripe tomatoes. It turns out all I need is to keep the simple kitchen scrap that I've been throwing away for years – <strong>fish scraps</strong>. </p><p>Here's what happened when I gave it a try.</p><h2 id="how-does-it-work-3">How does it work?</h2>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@selfsufficientme/video/7200767761713204485" data-video-id="7200767761713204485" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@selfsufficientme" href="https://www.tiktok.com/@selfsufficientme">@selfsufficientme</a>                            <p>What was the last experiment you did in your garden?</p><a target="_blank" title="♬ original sound  - Self Sufficient Me" href="https://www.tiktok.com/music/original-sound-Self-Sufficient-Me-7200768217325734662">♬ original sound  - Self Sufficient Me</a></section>                    </blockquote></div>                <p>TikTok creator, <a href="https://www.tiktok.com/@selfsufficientme/" target="_blank">@selfsufficientme</a> (also known as Mark Valencia) shares his gardening tips to over 230k followers and it's one of his most popular videos of all time with 3 million views and counting that I found particularly interesting – using fish scraps for great tomatoes.</p><p>In the video above, Mark carries out an experiment where he buries fish scraps in the soil alongside his tomato plants and then plants a section with no fish scraps to see the difference as they grow. For this, he used two fish heads and no other fertilizer for one and nothing at all for the other. He then pruned and watered the plants the same to see how they'd fare. </p><p>While he noticed that the no fish heads tomato plant was growing better to begin with, as well as having the first ripe tomatoes, it quickly changed. He says once they both produced tomatoes: "It was noticeable that the fish head tomatoes were also producing more fruit. After the five to six month mark in October, there was a considerable difference beginning to show between the two plants.</p><p>"The ones with the fish head were still growing strong with much better fruit and foliage growth than the ones without the fish head which were pale, dying back and had practically stopped producing new tomatoes."</p><p>The fish works as a fertilizer around the tomato plants roots and as it decomposes, it releases nutrients like phosphorus and potassium that the tomato plant can thrive on. While I thought it was strange that I'd never heard of it before, I figured it was time for me to give it a go. </p><h2 id="here-s-what-happened-when-i-tried-it-3">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="ybpqyfqtMJj82jKWwxVz94" name="tomato_plant_hack" alt="Soil for planting" src="https://cdn.mos.cms.futurecdn.net/ybpqyfqtMJj82jKWwxVz94.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>Admittedly, when I first saw Mark's video, I thought it was kind of gross. I don't tend to cut up whole raw fish regularly, but I do from time to time. Since I was planning on firing up my grill this summer, I figured it would be a good time to give it a go. </p><p>While I'd usually just opt to throw the fish scraps away quickly, this time I held on to them. </p><p>Yes, I was tempted to buy the fish emulsion fertilizer straight from the shops; I also knew from Mark's video that it would be better to try this out with fresh fish to hopefully get the best results (and that it would also be a lot cheaper). So, I chopped up some bits of fish and buried them down into the soil where I was planting my tomato plants.</p><div class="product"><a data-dimension112="1c9c8951-38b0-4fc4-9f9d-e36e6829f168" data-action="Deal Block" data-label="If you're looking to do an experiment of your own or don't tend to cut up raw fish, you can also buy a fish emulsion fertilizer that you dilute with some water. With this 16oz bottle, you can use it on plants both indoors and outdoors to help them grow." data-dimension48="If you're looking to do an experiment of your own or don't tend to cut up raw fish, you can also buy a fish emulsion fertilizer that you dilute with some water. With this 16oz bottle, you can use it on plants both indoors and outdoors to help them grow." data-dimension25="$12.40" href="https://www.amazon.com/Fertilome-10611-Emulsion-Fertilizer-5-1-1/dp/B0055SAEM6/" 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="qeuti7LefEi7xJfKaeo3nd" name="fish_fertilizer_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qeuti7LefEi7xJfKaeo3nd.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you're looking to do an experiment of your own or don't tend to cut up raw fish, you can also buy a fish emulsion fertilizer that you dilute with some water. With this 16oz bottle, you can use it on plants both indoors and outdoors to help them grow.<a class="view-deal button" href="https://www.amazon.com/Fertilome-10611-Emulsion-Fertilizer-5-1-1/dp/B0055SAEM6/" target="_blank" rel="nofollow" data-dimension112="1c9c8951-38b0-4fc4-9f9d-e36e6829f168" data-action="Deal Block" data-label="If you're looking to do an experiment of your own or don't tend to cut up raw fish, you can also buy a fish emulsion fertilizer that you dilute with some water. With this 16oz bottle, you can use it on plants both indoors and outdoors to help them grow." data-dimension48="If you're looking to do an experiment of your own or don't tend to cut up raw fish, you can also buy a fish emulsion fertilizer that you dilute with some water. With this 16oz bottle, you can use it on plants both indoors and outdoors to help them grow." data-dimension25="$12.40">View Deal</a></p></div><p>For now, I'm not at the stage where I can pass a judgment on how well my tomato plants are doing because it'll take some time. But, I can happily say that it was relatively fuss-free and I can now truly feel like I'm not wasting anything when buying a whole fish as I'm utilizing every single part of it. Plus, the grilled fish in itself was an extra treat.</p><p>So, it didn't cost me any additional cash to try it out and I'm pretty hopeful that from Mark's results, I'll be able to yield some lovely ripe tomatoes in a two to three months time. I'm just hoping they don't taste like fish when I get round to eating them...</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XZQZ5O"></div>                            </div>                            <script src="https://kwizly.com/embed/XZQZ5O.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/home/gardening/this-cheap-garden-hack-will-help-your-roses-grow-and-all-you-need-is-a-few-old-tea-bags">This cheap garden hack will help your roses grow — and all you need is a few old tea bags</a></li><li><a href="https://www.tomsguide.com/home/ive-just-discovered-this-strange-hack-thatll-keep-pests-away-from-your-plants-and-it-uses-a-popular-cold-medicine">I've just discovered this strange hack that'll keep pests away from your plants — and it uses a popular cold medicine</a></li><li><a href="https://www.tomsguide.com/home/gardening/this-garden-hack-will-help-your-plants-thrive-and-all-you-need-to-do-is-boil-some-eggs">This garden hack will help your plants thrive — and all you need to do is boil some eggs</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ 3 easy steps to protect visiting hummingbirds from the summer heat ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/protect-visiting-hummingbirds-from-summer-heat-in-just-3-easy-steps</link>
                                                                            <description>
                            <![CDATA[ Summer heat threatens hummingbirds, here's how to create a cooling refuge they'll rely on. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">QZDxxdo9zfM8PBbodKxopC</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/w9MRyn9MDqgssnk7VzFTPL-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 27 Jun 2026 08:00:00 +0000</pubDate>                                                                                                                                <updated>Mon, 29 Jun 2026 16:20:30 +0000</updated>
                                                                                                                                            <category><![CDATA[Home]]></category>
                                                                                                <author><![CDATA[ kaycee.hill@futurenet.com (Kaycee Hill) ]]></author>                    <dc:creator><![CDATA[ Kaycee Hill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xHn6RmpEqg87cvtLwrBu9G.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/w9MRyn9MDqgssnk7VzFTPL-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                        <media:description><![CDATA[&lt;p&gt;Hummingbird and red flower&lt;/p&gt;]]></media:description>                                                            <media:text><![CDATA[Hummingbird and red flower]]></media:text>
                                <media:title type="plain"><![CDATA[Hummingbird and red flower]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/w9MRyn9MDqgssnk7VzFTPL-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Summer heat poses a real threat to backyard hummingbirds. Because of their fast metabolisms, these tiny birds lose moisture rapidly when temperatures spike, and high heat can quickly cause your hummingbird nectar to ferment and spoil. </p><p>Unlike other species, hummingbirds cannot rely on shade alone to survive extreme weather, making cooling strategies absolutely essential. </p><p>If you want to know how to protect hummingbirds from summer heat, the solution is easier than you think. By making just three simple adjustments to your feeders and water features, you can easily transform your outdoor space into a safe, refreshing refuge while creating the <a href="https://www.tomsguide.com/home/create-the-ultimate-hummingbird-hangout-this-summer-5-essential-upgrades-starting-from-usd3">ultimate hummingbird hangout</a>. </p><p>These quick, low-effort changes will keep your local hummingbirds safe, hydrated, and fed all season long.</p><section class="howto-block">                    <h3>1. Provide cooling mist so they can bathe </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/5ki2Q3ukzDcAkLPfmwhTFo.jpg"                                        alt="Hummingbird and lilac flower"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/5ki2Q3ukzDcAkLPfmwhTFo.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Hummingbirds don't use traditional bird baths. They're too small and prefer mist to standing water. In summer heat especially, moving water with a light spray is crucial for keeping them cool and clean. </p><p><strong>Install a solar fountain in your existing bird bath or hang a solar misting station in a shaded area near your feeders</strong>. The movement and spray attract hummingbirds from a distance while providing the cooling they desperately need during hot afternoons. </p><p><strong>Clean your misting system every 2-3 days in summer </strong>to prevent algae growth and ensure the water stays fresh. Keep the water level topped up so the system runs continuously. Solar-powered options work best because they operate automatically, providing consistent cooling without requiring you to turn anything on or off.</p></p>                </section><div class="product"><a data-dimension112="3efc5d91-5d74-47bf-a021-9c89c7f37713" data-action="Deal Block" data-label="This two-in-one product will provide a place for your visiting hummingbirds to drink and cool down in the heat. The raised bird bath, with a 20-inch bowl, will keep them safe from prey, while the solar fountain will provide them with a fine mist of spray. For easy cleaning, the bowl can be unscrewed from the base." data-dimension48="This two-in-one product will provide a place for your visiting hummingbirds to drink and cool down in the heat. The raised bird bath, with a 20-inch bowl, will keep them safe from prey, while the solar fountain will provide them with a fine mist of spray. For easy cleaning, the bowl can be unscrewed from the base." data-dimension25="$47" href="https://www.amazon.com/VIVOHOME-Polyresin-Antique-Outdoor-Fountain/dp/B0876H67LP/ref=sr_1_20_sspa?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:590px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="b3tcDjx5yyS24GZTEPD7rW" name="Vivohome Bird Bath with Solar Fountain" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/b3tcDjx5yyS24GZTEPD7rW.jpg" mos="" align="middle" fullscreen="" width="590" height="590" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This two-in-one product will provide a place for your visiting hummingbirds to drink and cool down in the heat. The raised bird bath, with a 20-inch bowl, will keep them safe from prey, while the solar fountain will provide them with a fine mist of spray. For easy cleaning, the bowl can be unscrewed from the base.<a class="view-deal button" href="https://www.amazon.com/VIVOHOME-Polyresin-Antique-Outdoor-Fountain/dp/B0876H67LP/ref=sr_1_20_sspa?" target="_blank" rel="nofollow" data-dimension112="3efc5d91-5d74-47bf-a021-9c89c7f37713" data-action="Deal Block" data-label="This two-in-one product will provide a place for your visiting hummingbirds to drink and cool down in the heat. The raised bird bath, with a 20-inch bowl, will keep them safe from prey, while the solar fountain will provide them with a fine mist of spray. For easy cleaning, the bowl can be unscrewed from the base." data-dimension48="This two-in-one product will provide a place for your visiting hummingbirds to drink and cool down in the heat. The raised bird bath, with a 20-inch bowl, will keep them safe from prey, while the solar fountain will provide them with a fine mist of spray. For easy cleaning, the bowl can be unscrewed from the base." data-dimension25="$47">View Deal</a></p></div><section class="howto-block">                    <h3>2. Refresh feeders daily so nectar stays potent</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/jyfqJzPybUx5W8CPbFEXsU.jpg"                                        alt="Hummingbird and feeder"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/jyfqJzPybUx5W8CPbFEXsU.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Hummingbirds get virtually all their hydration from nectar, making fresh feeders absolutely critical during summer. In heat above 85°F, nectar ferments and spoils in just 24-48 hours. Spoiled nectar can harm hummingbirds or fail to provide the energy they need to regulate body temperature in extreme conditions.</p><p><strong>Empty, clean, and refill your feeders every single day during peak summer heat</strong>. Use a 4:1 water-to-sugar ratio (boil water first to remove impurities, dissolve sugar completely, then cool before filling). <strong>Never use honey, red dye, or artificial sweeteners </strong>as these can harm hummingbirds.</p><p><strong>Position your feeders in partial shade so nectar doesn't overheat sitting in direct sun</strong>. If you have multiple feeders, space them 3-4 feet apart to prevent territorial birds from monopolizing all food sources. This allows more hummingbirds to feed simultaneously, increasing their chances of finding energy-rich nectar when they need it most.</p></p>                </section><div class="product"><a data-dimension112="063c4a57-6551-4292-ba7c-a305140ba907" data-action="Deal Block" data-label="This bright red hummingbird feeder features six flower feeding ports, a hummingbird perch and an ingenious ant moat that hangs above to keep insects out of the feeder. Plus, to stop any sticky leaks, it has a built-in double gasket." data-dimension48="This bright red hummingbird feeder features six flower feeding ports, a hummingbird perch and an ingenious ant moat that hangs above to keep insects out of the feeder. Plus, to stop any sticky leaks, it has a built-in double gasket." data-dimension25="$14" href="https://www.amazon.com/gp/aw/d/B08W43F9TC/?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1458px;"><p class="vanilla-image-block" style="padding-top:102.88%;"><img id="ogW9bnqBs2aZzviXhZJ7bT" name="Hummingbird feeder" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ogW9bnqBs2aZzviXhZJ7bT.jpg" mos="" align="middle" fullscreen="" width="1458" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This bright red hummingbird feeder features six flower feeding ports, a hummingbird perch and an ingenious ant moat that hangs above to keep insects out of the feeder. Plus, to stop any sticky leaks, it has a built-in double gasket.<a class="view-deal button" href="https://www.amazon.com/gp/aw/d/B08W43F9TC/?" target="_blank" rel="nofollow" data-dimension112="063c4a57-6551-4292-ba7c-a305140ba907" data-action="Deal Block" data-label="This bright red hummingbird feeder features six flower feeding ports, a hummingbird perch and an ingenious ant moat that hangs above to keep insects out of the feeder. Plus, to stop any sticky leaks, it has a built-in double gasket." data-dimension48="This bright red hummingbird feeder features six flower feeding ports, a hummingbird perch and an ingenious ant moat that hangs above to keep insects out of the feeder. Plus, to stop any sticky leaks, it has a built-in double gasket." data-dimension25="$14">View Deal</a></p></div><section class="howto-block">                    <h3>3. Create shaded resting spots</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/YmEVmVK6myh7xSVdGjjjvB.jpg"                                        alt="Hummingbird drinking water"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/YmEVmVK6myh7xSVdGjjjvB.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Hummingbirds spend up to 80% of their day resting, not flying. During summer heat, they retreat to shade to conserve energy and lower their metabolic rate. Without adequate shaded perching spots, hummingbirds exhaust themselves trying to stay cool while flying.</p><p>Plant shrubs and small trees that provide dense foliage and natural perches.<strong> Position these in areas that receive morning sun but afternoon shade</strong> — the ideal microclimate during peak heat. Viburnums, lilacs, and flowering shrubs work perfectly because they provide both cooling shade and nectar.</p><p><strong>Install hummingbird perches or thin branches 3-10 feet from feeders, positioned where birds have clear views of all feeding areas</strong>. These perches let hummingbirds rest between feeding sessions while maintaining watch for predators and territorial competitors. Vary perching heights to accommodate different species' preferences.</p><p>Consider adding pergolas or shade structures near your feeders. Even temporary solutions like strategically placed shade cloth can provide relief during particularly brutal heat waves.</p></p>                </section><div class="product"><a data-dimension112="97bbc630-1c2a-45df-82b7-0d34143e2638" data-action="Deal Block" data-label="This pack of three swings is designed for hummingbirds to perch on. The metal swings have a wooden perch and come in three attractive designs, each with a metal chain and hook for attaching to a branch. Place them near your patio or hummingbird feeder and watch the birds have fun." data-dimension48="This pack of three swings is designed for hummingbirds to perch on. The metal swings have a wooden perch and come in three attractive designs, each with a metal chain and hook for attaching to a branch. Place them near your patio or hummingbird feeder and watch the birds have fun." href="https://www.amazon.com/Hummingbird-Swing-Perch-Wooden-Outdoor/dp/B09F5YG5CW/ref=sr_1_9?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1526px;"><p class="vanilla-image-block" style="padding-top:100.07%;"><img id="nxBgkkpPmfhgts3dr4SXB6" name="Mixxidea hummingbird perches" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/nxBgkkpPmfhgts3dr4SXB6.jpg" mos="" align="middle" fullscreen="" width="1526" height="1527" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This pack of three swings is designed for hummingbirds to perch on. The metal swings have a wooden perch and come in three attractive designs, each with a metal chain and hook for attaching to a branch. Place them near your patio or hummingbird feeder and watch the birds have fun.<a class="view-deal button" href="https://www.amazon.com/Hummingbird-Swing-Perch-Wooden-Outdoor/dp/B09F5YG5CW/ref=sr_1_9?" target="_blank" rel="nofollow" data-dimension112="97bbc630-1c2a-45df-82b7-0d34143e2638" data-action="Deal Block" data-label="This pack of three swings is designed for hummingbirds to perch on. The metal swings have a wooden perch and come in three attractive designs, each with a metal chain and hook for attaching to a branch. Place them near your patio or hummingbird feeder and watch the birds have fun." data-dimension48="This pack of three swings is designed for hummingbirds to perch on. The metal swings have a wooden perch and come in three attractive designs, each with a metal chain and hook for attaching to a branch. Place them near your patio or hummingbird feeder and watch the birds have fun." data-dimension25="">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XpmN8e"></div>                            </div>                            <script src="https://kwizly.com/embed/XpmN8e.js" async></script><figure class="van-image-figure pull-right inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:676px;"><p class="vanilla-image-block" style="padding-top:31.51%;"><img id="8RpLeaq6UZxfGpTcqGj7HX" name="Google" alt="Google" src="https://cdn.mos.cms.futurecdn.net/8RpLeaq6UZxfGpTcqGj7HX.jpg" mos="" align="right" fullscreen="" width="676" height="213" attribution="" endorsement="" class="pull-rightinline"></p></div></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/want-more-hummingbirds-7-tips-for-attracting-them-to-your-yard">Want more hummingbirds? 7 tips for attracting them to your yard</a></li><li><a href="https://www.tomsguide.com/home/outdoors/how-to-feed-hummingbirds-the-right-way-according-to-experts">This simple nectar recipe will attract hummingbirds to your yard — an expert reveals all</a></li><li><a href="https://www.tomsguide.com/home/want-more-songbirds-visiting-7-changes-that-bring-them-to-your-yard">Want more songbirds visiting? 7 changes that bring them to your yard</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I'm styling up my kitchen for less with these 9 last-minute Prime Day Le Creuset deals — save up to $125 ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/home-appliances/im-styling-up-my-kitchen-for-less-with-these-9-last-minute-prime-day-le-creuset-deals-save-up-to-usd125</link>
                                                                            <description>
                            <![CDATA[ If you're looking for cookware that will last a lifetime and add a classic touch to your kitchen, this Le Creuset sale is for you. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">LJSSyLmWaJ2mU3uTe5U4ng</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/hJjdssoKn2tXbN6MqwrYVf-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 26 Jun 2026 15:35:39 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Home]]></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/hJjdssoKn2tXbN6MqwrYVf-1280-80.jpg">
                                                            <media:credit><![CDATA[Le Creuset]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of Le Creuset items in a kitchen setting.]]></media:description>                                                            <media:text><![CDATA[A selection of Le Creuset items in a kitchen setting.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of Le Creuset items in a kitchen setting.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/hJjdssoKn2tXbN6MqwrYVf-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It's the final day of Amazon's big sales event, which means you only have a few hours left to take advantage of the <a href="https://www.tomsguide.com/live/news/last-minute-prime-day-deals-picked-by-editors-2026#live">Prime Day deals</a>. We've got a fantastic one for you here, though, centered on one of the most iconic and coveted homeware brands in the business: Le Creuset. </p><p>This prestige brand has been making world-class pans, skillets, and dishes for more than 100 years. Le Creuset is famous for its tough and stylish cast iron cookware in a range of beautiful colors that add a touch of class to any kitchen. </p><p>Now, these are undeniably quite pricey pieces, but you're buying quality that, with proper care, will last a lifetime. And with Prime Day now on, this is a perfect opportunity to make some massive savings.</p>        <div class="featured_product_block featured_block_hero" data-id="5ca29cfd-c3f6-4beb-ac32-aaef6ec29622">            <a href="https://www.amazon.com/Creuset-Revolution-Wood-Slotted-Spoon/dp/B007EFBXAU/" data-model-name="Le Creuset Slotted Spoon" 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/xrh969SpqyFvfhgJLk74vh.jpg" alt="Le Creuset Revolution Slotted Spoon, 12.5" X 2.5", Wood"></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">Le Creuset Slotted Spoon</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4dd453c9-7921-4d0d-8952-c56784a6aefb">            <a href="https://www.amazon.com/Creuset-Enamel-Steel-Kettle-Stoneware/dp/B08D9W6QGB/" data-model-name="Le Creuset Enamel on Steel Kettle & Stoneware Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:66.60%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Zk8FXeezKw8nKJ3iPuzuyj.jpg" alt="Le Creuset Enamel on Steel Demi Kettle & Stoneware Set of 2 Mugs, 1.25 Qt. Kettle & (2) 14 Oz. Mugs, White"></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">Le Creuset Enamel on Steel Kettle & Stoneware Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="523f6775-9199-48f7-9039-c045deb73fbe">            <a href="https://www.amazon.com/Creuset-Outdoor-Collection-Enameled-Skillet/dp/B0CPJTHVXD/" data-model-name="Le Creuset Cast Iron Skillet" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:52.40%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/N3FxC5pXMgybFgEr78uYpm.jpg" alt="Le Creuset Alpine Outdoor Collection Enameled Cast Iron Skillet, 10""></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">Le Creuset Cast Iron Skillet</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="021769ef-aabd-4e8f-82e2-f4b77f0ddc5c">            <a href="https://www.amazon.com/Creuset-Classic-Toughened-Nonstick-Paella/dp/B0GWFDJFC9/" data-model-name="Le Creuset Nonstick Paella Pan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:66.60%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/YW8hBZqrXFejXFr2R5jTc.jpg" alt="Le Creuset Classic Toughened Nonstick 12 In. Paella Pan"></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">Le Creuset Nonstick Paella Pan</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c4033534-d81b-4d37-8465-6cf30ecd003b">            <a href="https://www.amazon.com/Creuset-Enamel-Signature-Baker-Caribbean/dp/B0117T6T3E/" data-model-name="Le Creuset Cast Iron Signature Oval Baker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:66.60%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/mnhsxWjCZUxqRzgGuh3E94.jpg" alt="Le Creuset Enamel Cast Iron Signature Oval Baker, 3 Quart, Caribbean"></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">Le Creuset Cast Iron Signature Oval Baker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="77a0bab2-afc2-4ed4-85b7-894d1c377d8a">            <a href="https://www.amazon.com/Creuset-Enameled-Traditional-Frying-Oyster/dp/B0GWF9NNLR/" data-model-name="Le Creuset Enameled Cast Iron Frying Pan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:66.60%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/bcMRHYsBnF9HPXATut9NM7.jpg" alt="Le Creuset Enameled Cast Iron Traditional Round Frying Pan, 11.25 In., Oyster"></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">Le Creuset Enameled Cast Iron Frying Pan</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="0bf32205-b987-482e-b254-827a1e898e1c">            <a href="https://www.amazon.com/Creuset-Enameled-Signature-Sauteuse-White/dp/B086H3KKK3/" data-model-name="Le Creuset Cast Iron Signature Sauteuse Oven" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:66.60%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ypAkFYcTGuU7DHYR5kgoe8.jpg" alt="Le Creuset Enameled Cast Iron Signature Sauteuse Oven, 3.5 Qt., White"></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">Le Creuset Cast Iron Signature Sauteuse Oven</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="536dbeba-cb63-462c-adce-890bbf6a698c">            <a href="https://www.amazon.com/Cast-3-75-quart-Shallot-Cassadou-Creuset/dp/B0FLW5YMZ2/" data-model-name="Le Creuset Cast Iron Signature Cassadou" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:66.60%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/7pGvxD6FCpJQFMN359ECn9.jpg" alt="Le Creuset Enameled Cast Iron Signature Cassadou, 3.75 Qt., Shallot"></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">Le Creuset Cast Iron Signature Cassadou</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="5bb80242-1aa3-407d-b2ca-552629f95d4c">            <a href="https://www.amazon.com/Creuset-Signature-Chefs-7-5qt-Flame/dp/B0BTJCNBJH/" data-model-name="Le Creuset Signature Cast Iron Chef's 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/5jicdeV5N9zyvpNUmAii2C.jpg" alt="Le Creuset Signature Cast Iron Chef's Oven, 7.5qt, Flame"></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">Le Creuset Signature Cast Iron Chef's Oven</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-home-appliance-deals"><span>We're tracking all the best Prime Day home appliance deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Home+Appliances&price=50_&min_discount_ratio=0.95&offer_type=all&rows=6&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.&show_countdown=true&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 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[ Why do I have yellow patches in my lawn? Gardening experts share their top tips on how to fix it ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/why-do-i-have-yellow-patches-in-my-lawn-gardening-experts-share-their-top-tips-on-how-to-fix-it</link>
                                                                            <description>
                            <![CDATA[ Do you have unsightly yellow patches ruining your grass? Gardeners reveal the possible reasons why and how to fix it. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">kAymuRVnGuJ2L9D9NnqARU</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/tyBNke4AjR6gYGGH8NUiDZ-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 26 Jun 2026 04:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                                    <dc:creator><![CDATA[ Cynthia Lawrence ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/CCuSgQpd5NyZ46CgoF9cva.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/tyBNke4AjR6gYGGH8NUiDZ-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Brown patches in grass]]></media:description>                                                            <media:text><![CDATA[Brown patches in grass]]></media:text>
                                <media:title type="plain"><![CDATA[Brown patches in grass]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/tyBNke4AjR6gYGGH8NUiDZ-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Summer is here, and we’re not the only ones who are feeling parched in the heat. If you take pride in your lush and <a href="https://www.tomsguide.com/how-to/how-to-make-grass-greener">green grass</a>, it can be disheartening when you notice yellow patches suddenly appearing that quickly ruin the overall look. </p><p>This is especially the case if you’re hosting those backyard barbecues or gatherings. So if you’ve been watering and maintaining your yard, you’re probably wondering why you have yellow patches in your lawn. </p><p>We’ve called on the gardening and lawn care experts at hand to share their top tips on how to get rid of those discolored spots on your lawn and what causes them in the first place. So, read on if you want to revive and bring your lawn back to life this summer.</p><h2 id="what-causes-the-yellow-patches-in-my-lawn">What causes the yellow patches in my lawn?</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:5390px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RwnyCYupmo4CsDXuzHHtpJ" name="shutterstock_2249537999.jpg" alt="Brown spots in grass in garden" src="https://cdn.mos.cms.futurecdn.net/RwnyCYupmo4CsDXuzHHtpJ.jpg" mos="" align="middle" fullscreen="" width="5390" height="3032" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Brown patches in grass </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>Essentially, a yellowing lawn is common when the weather switches from heavy rain to a sudden dry spell. Scientifically, this yellowing is known as chlorosis. </p><p>Particularly, when grass undergoes stress, the green chlorophyll breaks down, and this process exposes the underlying yellow carotenoid pigments that are typically concealed during healthier periods.<strong> </strong></p><p>According to experts, there are various causes for your lackluster lawn. One obvious reason could be not watering your lawn adequately during hot spells, which can dry out the soil. Ideally, you should water deeply and infrequently, as this helps the roots grow stronger and penetrate deeper into the soil.</p><p>“If you notice yellow patches in your lawn, it might be a sign that you are overwatering or underwatering your grass,” said Allison Koenig, a lawn and garden expert at <a href="https://www.tractorsupply.com/" target="_blank">Tractor Supply</a>.</p><p>Basically, if your grass looks dry, turns brown, or has a crispy texture to it, you're likely under-watering, and your grass is parched. But if the grass is constantly soggy, has yellowing patches, or shows signs of mold or fungus, it’s a clear sign of overwatering, and you should reduce the amount of water your lawn is getting. </p><p>“The best way to tell what your lawn needs is through a quick soil check," Koenig said. "If it feels compacted and doesn’t absorb water well, you may be overwatering, while a dry, hard soil signals that your lawn needs more moisture.”</p><div><blockquote><p>Pet urine can also cause those yellow or brown spots on your lawn because of the high nitrogen levels, which essentially burn the grass.</p><p>Allison Koenig, lawn and garden expert </p></blockquote></div><p>Another reason could be diseases or pests, in particular chaffer grubs and leatherjackets that are known to feed directly on the grass roots, literally sucking out the water and essential nutrients it needs to thrive.  </p><p>“As they feed directly on the roots of the grass, they sever your lawn’s connection to its water and nutrient supply,” adds Angelika Zaber, Lawn Care Specialist and Gardening Expert working for <a href="https://www.onlineturf.co.uk" target="_blank">Online Turf.</a>  </p><p>“In terms of diseases, Fusarium is a common culprit since it often shows up as small, yellow, circular patches. To treat it, reduce the amount of thatch and loosen the soil by scarifying and aerating your lawn. Additionally, avoid high-nitrogen fertilizers. Instead, use a fertilizer that is low in nitrogen but high in potassium.”</p><p>Furthermore, for those with furry companions, discolored spots frequently stem from animal waste. “Pet urine can also cause those yellow or brown spots on your lawn because of the high nitrogen levels, which essentially burn the grass,” explained Koenig.</p><p>“To prevent these spots, one effective approach is to immediately water the area where your pet has urinated," Koenig said. "This dilutes the nitrogen and prevents it from "burning" the grass, preventing the brown spots from appearing. You can also try training your pet to use a designated area of the yard, such as a gravel or mulch patch, where the urine won’t harm your lawn.” </p><h2 id="how-do-i-fix-any-yellow-patches">How do I fix any yellow patches?</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:4345px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4obTEfSTnQVSYL2uvtvu8G" name="Main.jpg" alt="A thatching rake removing thatch from the grass" src="https://cdn.mos.cms.futurecdn.net/4obTEfSTnQVSYL2uvtvu8G.jpg" mos="" align="middle" fullscreen="" width="4345" height="2444" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">A thatching rake removing thatch from the grass </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>First, it’s advisable to start with a soil test to help you determine the pH levels and what nutrients it’s deficient in. </p><p>"This will help you determine the pH levels of the soil as well as if it has adequate nutrients and organic matter present," said Theresa Smith, the SVP at NaturaLawn of America.</p><p>“If the soil test results show signs of the soil having adequate nutrient content, then you know the yellowing or browning of the grass is being caused by an environmental factor or is under physical stress”.<strong> </strong></p><p>So, once you have established the cause for your discolored grass, how do you fix your unsightly patches? "The best way to fix this is by overseeding,” advises Zaber.</p><p>“Overseeding is a practice of spreading grass seed onto an already existing lawn to thicken it and give it that healthy, green appearance. Start by mowing, then scarifying and aerating the soil. </p><p>“Then, spread the seed at a rate of 25 grams per square meter and add a light layer of topsoil on top. This will improve the seed-to-soil contact, which is critical for good germination rates, and also protect the seeds from being eaten by birds or blown away by wind.”</p><p>Bear in mind that you may need a period of two to four weeks for initial growth, though achieving a fully restored, and lush lawn can take up to six months.“Grass usually takes around 2-4 weeks to germinate, depending on the weather. However, to fully establish, it can take up to 6 months,” added Zaber.</p><p>For more top tips, check out <a href="https://www.tomsguide.com/home/lawn-care/how-to-tell-if-your-lawn-needs-overseeding-3-signs-you-cant-ignore">how to tell if your lawn needs overseeding</a>, especially if you want to maintain healthy grass all year round. Or if you just want a lush lawn, try these <a href="https://www.tomsguide.com/home/lawn-care/11-secrets-to-a-lawn-thats-greener-than-your-neighbors">11 secrets to a lawn that’s greener than your neighbor's.</a></p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eG0N2W"></div>                            </div>                            <script src="https://kwizly.com/embed/eG0N2W.js" async></script><h3 class="article-body__section" id="section-lawncare-essentials-we-love"><span>Lawncare essentials we love</span></h3>        <div class="featured_product_block featured_block_hero" data-id="908cae43-55fa-4da3-8574-24b29266d738">            <a href="https://www.amazon.com/Garden-Guru-Handheld-Spreader-Shaker/dp/B0F8P8QM3V/ref=sr_1_8?" data-model-name="Garden Guru Handheld Seed Spreader Shaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:82.80%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ezrwVY6BYsgBZtBcNhMTm9.jpg" alt="Garden Guru Handheld Seed Spreader Shaker – Multi Use Grass Seed, Fertilizer, Salt, Ice & Snow Melt Spreader – Adjustable Twist Cap W Multiple Size Openings – Lightweight 1l Capacity Hand Seeder"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon</div>                                        <div class="featured__title">Garden Guru Handheld Seed Spreader Shaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="f740eec9-fe02-43b7-b239-5b64bced9d51">            <a href="https://www.amazon.com/dp/B07BD2GZW8" data-model-name="Garden Gloves" 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/YyBJP4YUpLVNV4q2yKFfjS.jpg" alt="Amazing Stuff For You green and gray gardening gloves"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazing Stuff</div>                                        <div class="featured__title">Garden Gloves</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="36c3c5ee-d7ff-4592-9d90-3e70c1b065ba">            <a href="https://www.amazon.com/Grampas-Weeder-CW-01-Original-Remover/dp/B001D1FFZA?" data-model-name="Grampa's Weeder" 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/kP7pgu2tpxz8d34HtVQgxC.jpg" alt="Grampa's Weeder The Original Stand Up Weed Puller Tool with Long Handle"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Grampa's Weeder</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="e9b94b08-caad-456c-b104-34a12eed1cce">            <a href="https://www.amazon.com/Greenworks-G-MAX-Mower-Batteries-Charger/dp/B086K59Q79/ref=sr_1_3?" data-model-name="Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower" 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/8LF2UvRs8EXvmebaZefwzA.jpg" alt="Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower (200+ Compatible Tools), (2) 4.0ah Batteries and Dual Port Rapid Charger Included"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Amazon</div>                                        <div class="featured__title">Greenworks 48v (24v X 2) 17" Cordless (push) Lawn Mower</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/how-to/7-common-lawn-care-mistakes-you-are-probably-making-right-now"><u>7 common lawn care mistakes you’re probably making right now</u></a></li><li><a href="https://www.tomsguide.com/how-to/9-ways-to-get-the-most-out-of-your-lawn-mower">9 ways to get the most out of your lawn mower</a></li><li><a href="https://www.tomsguide.com/how-to/5-plants-that-will-keep-wasps-out-of-your-yard">5 plants that will keep wasps out of your yard</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Hurry! Beat the heatwave with these portable air conditioner Prime Day deals before they sell out ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/home-appliances/im-melting-in-the-current-heatwave-so-ive-scoured-prime-day-for-the-portable-air-conditioner-deals-before-they-sell-out</link>
                                                                            <description>
                            <![CDATA[ The heatwaves coming to the United States and United Kingdom this summer will be much easier to face with a portable air conditioner at your side. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">Yf3XtHyYPKVFVPjnBUGhKf</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/os6P4LnUchXRHo86WhrkN3-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 24 Jun 2026 10:25:21 +0000</pubDate>                                                                                                                                <updated>Thu, 25 Jun 2026 09:39:12 +0000</updated>
                                                                                                                                            <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Home]]></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/os6P4LnUchXRHo86WhrkN3-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Portable air conditioner]]></media:description>                                                            <media:text><![CDATA[Portable air conditioner]]></media:text>
                                <media:title type="plain"><![CDATA[Portable air conditioner]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/os6P4LnUchXRHo86WhrkN3-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I don't need to tell you how uncomfortable it is to be stuck in a room with temperatures climbing past 30°C / 86°F and no air conditioning. We're only a couple of weeks into summer but Europe (where I am) is in the grip of a heatwave with temperatures hitting over 40°C / 104°F this week.</p><p>Things are no different in the United States. According to data shared by the National Weather Service’s (NWS) Climate Prediction Center (CPC), more than half of the country should brace for temperatures topping 100 degrees Fahrenheit in the coming days. And that's not even taking into account the humidity.</p><p>Naturally, the response is to crank up the A/C to stay cool. But if you don't already have one of the <a href="https://www.tomsguide.com/us/smart-air-conditioner-buying-guide,review-5615.html">best smart air conditioners</a> at home, it can be expensive and time-consuming to get one installed. The solution is to pick up a portable unit right now in the Amazon <a href="https://www.tomsguide.com/live/news/prime-day-2026-handpicked-editor-recommended-deals-live">Prime Day sales</a>. Not only can you save some cash, but chances are you can have it delivered and ready to go before the heatwave really hits.</p><p>I've scoured Amazon and found a few options worth considering if you've finally decided that the personal fan you bought weeks ago just ain't cutting it.</p><h3 class="article-body__section" id="section-us-portable-a-c-deals"><span>US portable A/C deals</span></h3><div class="product"><a data-dimension112="4e2763eb-ec9a-44d1-a955-d3951815a74b" data-action="Deal Block" data-label="This portable unit can cool rooms up to 36 sq ft in as little as 15 minutes. There's a user-friendly LED control panel, remote control, and automatic restart after a power outage. Put it in Sleep Mode and it dials the noise down to 52 dB so it keeps you cool at night without disturbing your sleep. While not silent, that's about on par with the level of noise in a quiet home anyway. Other features include a built-in fan (with cooling switched off) and a dehumidifier capable of removing 55L out of the air each day." data-dimension48="This portable unit can cool rooms up to 36 sq ft in as little as 15 minutes. There's a user-friendly LED control panel, remote control, and automatic restart after a power outage. Put it in Sleep Mode and it dials the noise down to 52 dB so it keeps you cool at night without disturbing your sleep. While not silent, that's about on par with the level of noise in a quiet home anyway. Other features include a built-in fan (with cooling switched off) and a dehumidifier capable of removing 55L out of the air each day." data-dimension25="$198" href="https://www.amazon.com/Coolblus-Portable-Conditioner-portable-Control/dp/B0CVTY66TM" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1499px;"><p class="vanilla-image-block" style="padding-top:100.07%;"><img id="zavu2WAindYYso3m7UY4rQ" name="61i02p19SaL._AC_SL1500_" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zavu2WAindYYso3m7UY4rQ.jpg" mos="" align="middle" fullscreen="" width="1499" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This portable unit can cool rooms up to 36 sq ft in as little as 15 minutes. There's a user-friendly LED control panel, remote control, and automatic restart after a power outage. Put it in Sleep Mode and it dials the noise down to 52 dB so it keeps you cool at night without disturbing your sleep. While not silent, that's about on par with the level of noise in a quiet home anyway. Other features include a built-in fan (with cooling switched off) and a dehumidifier capable of removing 55L out of the air each day.<a class="view-deal button" href="https://www.amazon.com/Coolblus-Portable-Conditioner-portable-Control/dp/B0CVTY66TM" target="_blank" rel="nofollow" data-dimension112="4e2763eb-ec9a-44d1-a955-d3951815a74b" data-action="Deal Block" data-label="This portable unit can cool rooms up to 36 sq ft in as little as 15 minutes. There's a user-friendly LED control panel, remote control, and automatic restart after a power outage. Put it in Sleep Mode and it dials the noise down to 52 dB so it keeps you cool at night without disturbing your sleep. While not silent, that's about on par with the level of noise in a quiet home anyway. Other features include a built-in fan (with cooling switched off) and a dehumidifier capable of removing 55L out of the air each day." data-dimension48="This portable unit can cool rooms up to 36 sq ft in as little as 15 minutes. There's a user-friendly LED control panel, remote control, and automatic restart after a power outage. Put it in Sleep Mode and it dials the noise down to 52 dB so it keeps you cool at night without disturbing your sleep. While not silent, that's about on par with the level of noise in a quiet home anyway. Other features include a built-in fan (with cooling switched off) and a dehumidifier capable of removing 55L out of the air each day." data-dimension25="$198">View Deal</a></p></div><div class="product"><a data-dimension112="2335f8c3-b914-4a4b-9ef2-133a0a8e7fcf" data-action="Deal Block" data-label="You can save $100 on this portable air conditioner that uses dual hoses for up to 60% faster cooling than a single hose alternative. Utilizing two hoses means the unit can have separate intake and exhaust hoses to cool rooms quickly and efficiently. This air conditioner is Wi-Fi enabled so you can fire it up from your smartphone (or smartwatch) without having to be at home. If you're out on the school pickup, you can have your kid's room down to a nice welcoming temperature by the time you get back. Midea has had to issue A/C unit recalls in the past, but this one comes with a one-year limited warranty from original purchase date." data-dimension48="You can save $100 on this portable air conditioner that uses dual hoses for up to 60% faster cooling than a single hose alternative. Utilizing two hoses means the unit can have separate intake and exhaust hoses to cool rooms quickly and efficiently. This air conditioner is Wi-Fi enabled so you can fire it up from your smartphone (or smartwatch) without having to be at home. If you're out on the school pickup, you can have your kid's room down to a nice welcoming temperature by the time you get back. Midea has had to issue A/C unit recalls in the past, but this one comes with a one-year limited warranty from original purchase date." data-dimension25="$479" href="https://www.amazon.com/dp/B0GYDZR5RX" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1144px;"><p class="vanilla-image-block" style="padding-top:131.12%;"><img id="RQGbUMowS32EJaxdkSTgve" name="midea" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/RQGbUMowS32EJaxdkSTgve.jpg" mos="" align="middle" fullscreen="" width="1144" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>You can save $100 on this portable air conditioner that uses dual hoses for up to 60% faster cooling than a single hose alternative. Utilizing two hoses means the unit can have separate intake and exhaust hoses to cool rooms quickly and efficiently. This air conditioner is Wi-Fi enabled so you can fire it up from your smartphone (or smartwatch) without having to be at home. If you're out on the school pickup, you can have your kid's room down to a nice welcoming temperature by the time you get back. Midea has had to issue A/C unit recalls in the past, but this one comes with a one-year limited warranty from original purchase date.<a class="view-deal button" href="https://www.amazon.com/dp/B0GYDZR5RX" target="_blank" rel="nofollow" data-dimension112="2335f8c3-b914-4a4b-9ef2-133a0a8e7fcf" data-action="Deal Block" data-label="You can save $100 on this portable air conditioner that uses dual hoses for up to 60% faster cooling than a single hose alternative. Utilizing two hoses means the unit can have separate intake and exhaust hoses to cool rooms quickly and efficiently. This air conditioner is Wi-Fi enabled so you can fire it up from your smartphone (or smartwatch) without having to be at home. If you're out on the school pickup, you can have your kid's room down to a nice welcoming temperature by the time you get back. Midea has had to issue A/C unit recalls in the past, but this one comes with a one-year limited warranty from original purchase date." data-dimension48="You can save $100 on this portable air conditioner that uses dual hoses for up to 60% faster cooling than a single hose alternative. Utilizing two hoses means the unit can have separate intake and exhaust hoses to cool rooms quickly and efficiently. This air conditioner is Wi-Fi enabled so you can fire it up from your smartphone (or smartwatch) without having to be at home. If you're out on the school pickup, you can have your kid's room down to a nice welcoming temperature by the time you get back. Midea has had to issue A/C unit recalls in the past, but this one comes with a one-year limited warranty from original purchase date." data-dimension25="$479">View Deal</a></p></div><div class="product"><a data-dimension112="0cae7079-756b-408c-a707-fa18428c1107" data-action="Deal Block" data-label="review of its predecessor" data-dimension48="review of its predecessor" data-dimension25="$799" href="https://www.amazon.com/EF-ECOFLOW-Portable-Conditioner-Charging/dp/B0F4D4Z18S" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1080px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="bUfXVZFX9nfgMgM7YetDVW" name="excoflow 3" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/bUfXVZFX9nfgMgM7YetDVW.jpg" mos="" align="middle" fullscreen="" width="1080" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This one kicks things up a notch in terms of pricing, but the discount is far greater. You can save 38% ($500) on this unit right now for Prime Day. We haven't tested this latest version out but in our <a href="https://www.tomsguide.com/reviews/ecoflow-wave-2" data-dimension112="0cae7079-756b-408c-a707-fa18428c1107" data-action="Deal Block" data-label="review of its predecessor" data-dimension48="review of its predecessor" data-dimension25="$799">review of its predecessor</a>, we noted the best thing about the Ecoflow is that it's actually two devices in one. That's the same for this newer model: While you can use it as a 6,100 BTU cooling portable air con machine in the summer, it pulls double duty as a 6,800 BTU heater in the winter. The battery life has been improved over the EcoWave 2 and it's also gotten quieter, operating at whisper-quiet 42 to 52 dB depending on the fan speed. If you really want to beat the heat (and the cold), this is the product to go for. <a class="view-deal button" href="https://www.amazon.com/EF-ECOFLOW-Portable-Conditioner-Charging/dp/B0F4D4Z18S" target="_blank" rel="nofollow" data-dimension112="0cae7079-756b-408c-a707-fa18428c1107" data-action="Deal Block" data-label="review of its predecessor" data-dimension48="review of its predecessor" data-dimension25="$799">View Deal</a></p></div><h3 class="article-body__section" id="section-uk-portable-a-c-deals"><span>UK portable A/C deals</span></h3><p>Unfortunately, finding deals on portable AC units in the U.K. is proving tough right now. Given the current state of the country — with a severe heat warning in place — many units are selling out fast. </p><p>While I haven't been able to find any specific deals worth your time, here are some units I've found that are still in stock. Although there's no telling how long they'll be available for so you'll need to be quick.</p><div class="product"><a data-dimension112="c9bf9bff-8aea-4d68-8bc2-8d059b5316ae" data-action="Deal Block" data-label="It's not a deal, but it is one of the cheapest portable air conditioners that I've seen over the last couple of days. And it's still in stock over at Lidl, but you'll need to check the store locator to see if you can grab one. Going by the specs sheet, this air conditioner is suitable for rooms between 30 and 45m and can set temperatures from 16 to 31°C. The cooling capacity is 7000 BTU and you've got a 1.5m drain hose and a window kit included for venting. At 65dB, it won't be quiet — but it will be cool and it will be cheap." data-dimension48="It's not a deal, but it is one of the cheapest portable air conditioners that I've seen over the last couple of days. And it's still in stock over at Lidl, but you'll need to check the store locator to see if you can grab one. Going by the specs sheet, this air conditioner is suitable for rooms between 30 and 45m and can set temperatures from 16 to 31°C. The cooling capacity is 7000 BTU and you've got a 1.5m drain hose and a window kit included for venting. At 65dB, it won't be quiet — but it will be cool and it will be cheap." data-dimension25="£149" href="https://www.lidl.co.uk/p/tronic-3-in-1-air-conditioner/p10050898" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1162px;"><p class="vanilla-image-block" style="padding-top:89.33%;"><img id="23tNr9n4gq94pvGwXkNP9X" name="lidle air conditoner" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/23tNr9n4gq94pvGwXkNP9X.jpg" mos="" align="middle" fullscreen="" width="1162" height="1038" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>It's not a deal, but it is one of the cheapest portable air conditioners that I've seen over the last couple of days. And it's still in stock over at Lidl, but you'll need to check the store locator to see if you can grab one. Going by the specs sheet, this air conditioner is suitable for rooms between 30 and 45m and can set temperatures from 16 to 31°C. The cooling capacity is 7000 BTU and you've got a 1.5m drain hose and a window kit included for venting. At 65dB, it won't be quiet — but it will be cool and it will be cheap.<a class="view-deal button" href="https://www.lidl.co.uk/p/tronic-3-in-1-air-conditioner/p10050898" target="_blank" rel="nofollow" data-dimension112="c9bf9bff-8aea-4d68-8bc2-8d059b5316ae" data-action="Deal Block" data-label="It's not a deal, but it is one of the cheapest portable air conditioners that I've seen over the last couple of days. And it's still in stock over at Lidl, but you'll need to check the store locator to see if you can grab one. Going by the specs sheet, this air conditioner is suitable for rooms between 30 and 45m and can set temperatures from 16 to 31°C. The cooling capacity is 7000 BTU and you've got a 1.5m drain hose and a window kit included for venting. At 65dB, it won't be quiet — but it will be cool and it will be cheap." data-dimension48="It's not a deal, but it is one of the cheapest portable air conditioners that I've seen over the last couple of days. And it's still in stock over at Lidl, but you'll need to check the store locator to see if you can grab one. Going by the specs sheet, this air conditioner is suitable for rooms between 30 and 45m and can set temperatures from 16 to 31°C. The cooling capacity is 7000 BTU and you've got a 1.5m drain hose and a window kit included for venting. At 65dB, it won't be quiet — but it will be cool and it will be cheap." data-dimension25="£149">View Deal</a></p></div><div class="product"><a data-dimension112="efb4970a-8c12-4606-8ece-6512ca5a0a8a" data-action="Deal Block" data-label="This guy probably won't stay in stock at Amazon for too long. It works as a dehumidifier and a regular cooling fan as well as its air conditioning duties. As with many of the other items on this list, it's got a programmable timer for you to set schedules by and comes with a window venting kit. It's got a 9,000 BTU capacity and noise levels will hit 65dB. Because it's also a dehumidifier, there's an on board HEPA filter. It's rated Energy class A, too." data-dimension48="This guy probably won't stay in stock at Amazon for too long. It works as a dehumidifier and a regular cooling fan as well as its air conditioning duties. As with many of the other items on this list, it's got a programmable timer for you to set schedules by and comes with a window venting kit. It's got a 9,000 BTU capacity and noise levels will hit 65dB. Because it's also a dehumidifier, there's an on board HEPA filter. It's rated Energy class A, too." data-dimension25="£699" href="https://www.amazon.co.uk/Belaco-Conditioning-Conditioner-Dehumidifier-ACU-9000R1/dp/B0G3R5JB8S" 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:87.47%;"><img id="QvyzNz2oQnkoE69BZ4mqTA" name="61u+LswWoGL._AC_SL1500_" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/QvyzNz2oQnkoE69BZ4mqTA.jpg" mos="" align="middle" fullscreen="" width="1500" height="1312" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This guy probably won't stay in stock at Amazon for too long. It works as a dehumidifier and a regular cooling fan as well as its air conditioning duties. As with many of the other items on this list, it's got a programmable timer for you to set schedules by and comes with a window venting kit. It's got a 9,000 BTU capacity and noise levels will hit 65dB. Because it's also a dehumidifier, there's an on board HEPA filter. It's rated Energy class A, too.<a class="view-deal button" href="https://www.amazon.co.uk/Belaco-Conditioning-Conditioner-Dehumidifier-ACU-9000R1/dp/B0G3R5JB8S" target="_blank" rel="nofollow" data-dimension112="efb4970a-8c12-4606-8ece-6512ca5a0a8a" data-action="Deal Block" data-label="This guy probably won't stay in stock at Amazon for too long. It works as a dehumidifier and a regular cooling fan as well as its air conditioning duties. As with many of the other items on this list, it's got a programmable timer for you to set schedules by and comes with a window venting kit. It's got a 9,000 BTU capacity and noise levels will hit 65dB. Because it's also a dehumidifier, there's an on board HEPA filter. It's rated Energy class A, too." data-dimension48="This guy probably won't stay in stock at Amazon for too long. It works as a dehumidifier and a regular cooling fan as well as its air conditioning duties. As with many of the other items on this list, it's got a programmable timer for you to set schedules by and comes with a window venting kit. It's got a 9,000 BTU capacity and noise levels will hit 65dB. Because it's also a dehumidifier, there's an on board HEPA filter. It's rated Energy class A, too." data-dimension25="£699">View Deal</a></p></div><div class="product"><a data-dimension112="ce80bfee-db39-4852-ac0f-26a0591b2274" data-action="Deal Block" data-label="B&amp;Q currently have this unit in stock, although delivery isn't an option — you'll have to go to your nearest outlet and pick it up yourself. This is a 9,000BTU unit that comes with a window insulation kit and three different airflow speeds. It can run automatically with a 24-hour timer or you can control it manually with the included remote control. It's got an Energy class A rating and a 2-year guarantee." data-dimension48="B&amp;Q currently have this unit in stock, although delivery isn't an option — you'll have to go to your nearest outlet and pick it up yourself. This is a 9,000BTU unit that comes with a window insulation kit and three different airflow speeds. It can run automatically with a 24-hour timer or you can control it manually with the included remote control. It's got an Energy class A rating and a 2-year guarantee." data-dimension25="£299" href="https://www.diy.com/departments/goodhome-culver-air-conditioner-220-240v-9000btu/5063022801753_BQ.prd" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1396px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="Gq3sEcbo2Zj96F7q6xmPkc" name="goodhome ac" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Gq3sEcbo2Zj96F7q6xmPkc.jpg" mos="" align="middle" fullscreen="" width="1396" height="1396" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>B&Q currently have this unit in stock, although delivery isn't an option — you'll have to go to your nearest outlet and pick it up yourself. This is a 9,000BTU unit that comes with a window insulation kit and three different airflow speeds. It can run automatically with a 24-hour timer or you can control it manually with the included remote control. It's got an Energy class A rating and a 2-year guarantee.<a class="view-deal button" href="https://www.diy.com/departments/goodhome-culver-air-conditioner-220-240v-9000btu/5063022801753_BQ.prd" target="_blank" rel="nofollow" data-dimension112="ce80bfee-db39-4852-ac0f-26a0591b2274" data-action="Deal Block" data-label="B&amp;Q currently have this unit in stock, although delivery isn't an option — you'll have to go to your nearest outlet and pick it up yourself. This is a 9,000BTU unit that comes with a window insulation kit and three different airflow speeds. It can run automatically with a 24-hour timer or you can control it manually with the included remote control. It's got an Energy class A rating and a 2-year guarantee." data-dimension48="B&amp;Q currently have this unit in stock, although delivery isn't an option — you'll have to go to your nearest outlet and pick it up yourself. This is a 9,000BTU unit that comes with a window insulation kit and three different airflow speeds. It can run automatically with a 24-hour timer or you can control it manually with the included remote control. It's got an Energy class A rating and a 2-year guarantee." data-dimension25="£299">View Deal</a></p></div><div class="product"><a data-dimension112="3bd73241-809b-4013-b748-39c860a9a3ef" data-action="Deal Block" data-label="The MC Series Pro 9000BTU air conditioner is built to handle rooms up to 26m. Like others on this list, it can also double up as a heater as well as packing in a dehumidifier. It's quiet at 51dB and A-rated for energy efficiency, it costs around 25p/hour to run. You can operate it via the Meaco App or remote for convenience. Includes versatile window kits and a two-year warranty." data-dimension48="The MC Series Pro 9000BTU air conditioner is built to handle rooms up to 26m. Like others on this list, it can also double up as a heater as well as packing in a dehumidifier. It's quiet at 51dB and A-rated for energy efficiency, it costs around 25p/hour to run. You can operate it via the Meaco App or remote for convenience. Includes versatile window kits and a two-year warranty." data-dimension25="£400" href="https://www.argos.co.uk/product/7623899" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1202px;"><p class="vanilla-image-block" style="padding-top:69.55%;"><img id="N5T3hTtUmwX7U7sCCBPfaM" name="air con 3" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/N5T3hTtUmwX7U7sCCBPfaM.jpg" mos="" align="middle" fullscreen="" width="1202" height="836" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The MC Series Pro 9000BTU air conditioner is built to handle rooms up to 26m. Like others on this list, it can also double up as a heater as well as packing in a dehumidifier. It's quiet at 51dB and A-rated for energy efficiency, it costs around 25p/hour to run. You can operate it via the Meaco App or remote for convenience. Includes versatile window kits and a two-year warranty.<a class="view-deal button" href="https://www.argos.co.uk/product/7623899" target="_blank" rel="nofollow" data-dimension112="3bd73241-809b-4013-b748-39c860a9a3ef" data-action="Deal Block" data-label="The MC Series Pro 9000BTU air conditioner is built to handle rooms up to 26m. Like others on this list, it can also double up as a heater as well as packing in a dehumidifier. It's quiet at 51dB and A-rated for energy efficiency, it costs around 25p/hour to run. You can operate it via the Meaco App or remote for convenience. Includes versatile window kits and a two-year warranty." data-dimension48="The MC Series Pro 9000BTU air conditioner is built to handle rooms up to 26m. Like others on this list, it can also double up as a heater as well as packing in a dehumidifier. It's quiet at 51dB and A-rated for energy efficiency, it costs around 25p/hour to run. You can operate it via the Meaco App or remote for convenience. Includes versatile window kits and a two-year warranty." data-dimension25="£400">View Deal</a></p></div><h3 class="article-body__section" id="section-what-to-know-before-buying"><span>What to know before buying</span></h3><p>The main thing you want to look at before buying a portable air conditioner is the BTU rating. That stands for British Thermal Unit and indicates the cooling capacity. In effect, how much heat it can get rid of in a room over the course of one hour.</p><p>The higher the number, the more powerful the cooling. Anything that's rated 5,000 BTUs and above will be more than sufficient to cool a small-to-medium room. The only trade-off you need to be aware of is noise levels, particularly if you plan to run the air con while you sleep. Anything over 65 decibels is likely to become a disturbance during the night.</p><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-deals"><span>We're tracking all the best Prime Day deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Everything&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-right {      position: absolute;      right: 8px;      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-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right {      right: 0;      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-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">          \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.previousElementSibling.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-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>\n        \x3C/div>\n      \x3C/div>\n    \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">      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>    \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';             }          }                    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');                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')}`                });              }            });          }        }        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';          }        }        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;                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.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12;          } else {             this.displayLimit += (this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12;          }                    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 encodedTag = encodeURIComponent(this.activeDealTag.toLowerCase().replace(/\s+/g, '-'));             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');                    if (this.rowsSelect && this.rowsSelect.value) {            url.searchParams.append('rows', this.rowsSelect.value);          } else {             url.searchParams.append('rows', '12'); // default          }          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 = 12;          if (this.rowsSelect && this.rowsSelect.value) {            rowLimit = parseInt(this.rowsSelect.value, 10) || 12;          }          // 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 => {              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}`;                }              }              return `                \x3Ca href="${this.escapeHTML(linkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item">                  ${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);                }              });            });                                  } 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 = `              \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;                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let existingChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             if (this.getViewMode() === 'carousel') {                 if (!existingChevron) {                     gridWrapper.insertAdjacentHTML('beforeend', '\n                 \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.previousElementSibling.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>');                 }             } else {                 if (existingChevron) {                     existingChevron.remove();                 }             }          }                    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.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12);                 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 the viral Ninja Creami and it's absolutely worth it, especially at an all-time low for Prime Day ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/home-appliances/i-tried-the-viral-ninja-creami-and-its-absolutely-worth-it-especially-at-an-all-time-low-for-prime-day</link>
                                                                            <description>
                            <![CDATA[ Our favorite Ninja Creami ice cream maker has just dropped in price for Prime Day — but you’d better hurry. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">o8CPADmTi8YFLzppWVLM2P</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/Tsti2t5o5cSSrbRRpKqdNn-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 23 Jun 2026 20:24:54 +0000</pubDate>                                                                                                                                <updated>Tue, 23 Jun 2026 20:29:51 +0000</updated>
                                                                                                                                            <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                                                                                    <dc:creator><![CDATA[ Alyse Stanley ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/BxNnQuBWRHqkv5xWZsjrjc.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Alyse Stanley is a news editor at Tom’s Guide, overseeing weekend coverage and writing about the latest in tech, gaming, and entertainment. Before Tom’s Guide, Alyse worked as an editor for the Washington Post’s sunsetted video game section, Launcher, where she also wrote about indie games you shouldn’t miss, how to tackle your gaming backlog, and all things Nintendo. She previously led Gizmodo’s weekend news desk covering breaking tech news and has written game reviews and features for outlets like Polygon, Unwinnable, and Rock, Paper, Shotgun. A recent Chicago-area transplant born and raised in Virginia, Alyse is a big fan of horror movies, cartoons, and roller skating. She&#039;s also a puzzle fan and can often be found contributing to the NYT Connections coverage on Tom&#039;s Guide &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/Tsti2t5o5cSSrbRRpKqdNn-1280-80.jpg">
                                                            <media:credit><![CDATA[Ninja]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Ninja Creami as seen on a countertop with a Tom&#039;s Guide Savings Squad Prime Day badge]]></media:description>                                                            <media:text><![CDATA[Ninja Creami as seen on a countertop with a Tom&#039;s Guide Savings Squad Prime Day badge]]></media:text>
                                <media:title type="plain"><![CDATA[Ninja Creami as seen on a countertop with a Tom&#039;s Guide Savings Squad Prime Day badge]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/Tsti2t5o5cSSrbRRpKqdNn-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It's always the season for a sweet treat, especially with the sun threatening to boil us all alive this summer. For all my ice cream lovers out there, the Ninja Creami is a must-have. Not only has this viral ice cream maker become a cult favorite for making mouth-watering shakes, smoothies, and more, but it's also one of the most fun kitchen essentials out there. And If you've been waiting for a good deal, the <a href="https://www.tomsguide.com/live/news/prime-day-2026-handpicked-editor-recommended-deals-live">Prime Day sales</a> going on this week make now the perfect time to splurge. </p><p>Right now, the <a href="https://www.amazon.com/Ninja-CREAMi-Ice-Cream-Maker/dp/B09QV24FFZ?th=1" target="_blank" rel="nofollow">Ninja Creami is just $169 at Amazon</a> —  the lowest ever price we've seen on this ice cream maker. But you'd better act fast, because we've seen stock sell out before on Black Friday. And that was when it was $10 more. For even more savings, check out <a href="https://www.tomsguide.com/home/save-up-to-50-percent-on-my-favorite-kitchen-appliances-for-prime-day-ninja-slushi-keurig-kitchenaid-and-more">our favorite kitchen appliances on sale this Prime Day</a>. You can score up to 50% off brands that rarely go on sale like Keurig and KitchenAid to build your dream kitchen set-up. Just be sure to invite me if ice cream's on the menu. </p><div class="product"><a data-dimension112="77584463-b29f-470c-a749-64e655178888" data-action="Deal Block" data-label="With 7 pre-set programs and customizable options, the Ninja CREAMi Ice Cream Maker will help you whip up gelato, milkshakes, smoothies and more, all with your favorite nuts, candy and fruit to personalize your treat. It also comes with 2 pint pots with lids. For Prime Day, it's marked down to $169 — the lowest we've seen it yet." data-dimension48="With 7 pre-set programs and customizable options, the Ninja CREAMi Ice Cream Maker will help you whip up gelato, milkshakes, smoothies and more, all with your favorite nuts, candy and fruit to personalize your treat. It also comes with 2 pint pots with lids. For Prime Day, it's marked down to $169 — the lowest we've seen it yet." data-dimension25="$169" href="https://www.amazon.com/Ninja-CREAMi-Ice-Cream-Maker/dp/B09QV24FFZ" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:900px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="QZc33qvAZYWX8L676BCnTE" name="Ninja Creami NC301 deal block.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/QZc33qvAZYWX8L676BCnTE.jpg" mos="" align="middle" fullscreen="" width="900" height="900" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>With 7 pre-set programs and customizable options, the Ninja CREAMi Ice Cream Maker will help you whip up gelato, milkshakes, smoothies and more, all with your favorite nuts, candy and fruit to personalize your treat. It also comes with 2 pint pots with lids. For Prime Day, it's marked down to $169 — the lowest we've seen it yet. <a class="view-deal button" href="https://www.amazon.com/Ninja-CREAMi-Ice-Cream-Maker/dp/B09QV24FFZ" target="_blank" rel="nofollow" data-dimension112="77584463-b29f-470c-a749-64e655178888" data-action="Deal Block" data-label="With 7 pre-set programs and customizable options, the Ninja CREAMi Ice Cream Maker will help you whip up gelato, milkshakes, smoothies and more, all with your favorite nuts, candy and fruit to personalize your treat. It also comes with 2 pint pots with lids. For Prime Day, it's marked down to $169 — the lowest we've seen it yet." data-dimension48="With 7 pre-set programs and customizable options, the Ninja CREAMi Ice Cream Maker will help you whip up gelato, milkshakes, smoothies and more, all with your favorite nuts, candy and fruit to personalize your treat. It also comes with 2 pint pots with lids. For Prime Day, it's marked down to $169 — the lowest we've seen it yet." data-dimension25="$169">View Deal</a></p></div><h3 class="article-body__section" id="section-why-we-love-the-ninja-creami"><span>Why we love the Ninja Creami</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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5vhJdjp9G3jyoawWsG5ViT" name="Ninja creami next to ice cream" alt="Ninja Creami" src="https://cdn.mos.cms.futurecdn.net/5vhJdjp9G3jyoawWsG5ViT.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Ninja creami blends ice cream </span><span class="credit" itemprop="copyrightHolder">(Image credit: Ninja )</span></figcaption></figure><p>The Ninja Creami tops the list of the best home ice cream makers we've ever tested, and it's easy to see why. In our 4-star <a href="https://www.tomsguide.com/home/home-appliances/ninja-creami-review">Ninja Creami review</a>, its versatility seriously impressed us, and its intuitive design made it easy to whip up everything from rich gelato and refreshing sorbet to creamy milkshakes that put McDonalds to shame.</p><p>While you'll need to freeze your base for 24 hours before churning, which can be a pain, the Creami makes the rest of the process super simple. It also includes two pint containers, so it's easy to prep multiple flavors in advance.</p><p>What’s more, if you have dietary requirements, the Creami has several settings designed to fit different needs like Keto, vegan, and even fruitarians! Where it really shines, though, is with mix-ins and powders. Protein powder, instant coffee, matcha, chai latte powder, and the like blended smoothly into our recipes without the chalky texture that often plagues other blenders and ice cream makers.</p><p>If you've been thinking about adding a Ninja Creami to your kitchen, now is a great time to take advantage of a deal and start experimenting with homemade frozen treats. For more inspo, follow <a href="https://www.tomsguide.com/home/home-appliances/just-got-the-ninja-creami-follow-these-tips-for-perfect-results-every-time">these 3 tips for perfect results every time.</a></p><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Home+Appliances&rows=6&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.&show_header_details=false&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');                    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' ? '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 { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button: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';          }        }        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.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12;                    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 airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1781000519267.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 && 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 url = `https://airedale.futurecdn.net/feeds/feed_1781000519267.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 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');                    if (this.rowsSelect && this.rowsSelect.value) {            url.searchParams.append('rows', this.rowsSelect.value);          } else {             url.searchParams.append('rows', '12'); // default          }          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 = 12;          if (this.rowsSelect && this.rowsSelect.value) {            rowLimit = parseInt(this.rowsSelect.value, 10) || 12;          }          // 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;">` : ''}                            \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;">` : ''}                          \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');            if (!this.processArticleFired) {                  this.processArticleFired = true;                  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 += 12;              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[ Ninja just launched the Crispi DualZone air fryer — and it's got the feature I've been desperately waiting for ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/kitchen-dining/ninja-just-launched-the-crispi-dualzone-air-fryer-and-its-got-the-feature-ive-been-desperately-waiting-for</link>
                                                                            <description>
                            <![CDATA[ The Ninja Crispi DualZone air fryer lets you cook your food in two independent zones. It's what I've been waiting for, and here's why. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">oi4tQj8Jy7AVzM8DS23Fsc</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/oNXg5PjjCkbsi9EG7fRd8c-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 23 Jun 2026 12:23:19 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Kitchen &amp; 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/oNXg5PjjCkbsi9EG7fRd8c-1280-80.jpg">
                                                            <media:credit><![CDATA[Ninja]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Ninja Crispi DualZone being used]]></media:description>                                                            <media:text><![CDATA[The Ninja Crispi DualZone being used]]></media:text>
                                <media:title type="plain"><![CDATA[The Ninja Crispi DualZone being used]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/oNXg5PjjCkbsi9EG7fRd8c-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Ninja has got quite the hold on the air fryer world, dominating the space (and our <a href="https://www.tomsguide.com/best-picks/best-air-fryers">best air fryers</a> guide) with the <a href="https://www.tomsguide.com/home/ninja-air-fryer-pro-in-1-review">Pro 4-in-1</a> and <a href="https://www.tomsguide.com/reviews/ninja-foodi-dualzone-air-fryer-dz701">Foodi DualZone</a>. When the <a href="https://www.tomsguide.com/home/air-fryers/ninja-crispi-air-fryer-review">Ninja Crispi </a>air fryer was released in 2025, it even prompted our expert tester to say, "It made me rethink everything I knew about air fryers." </p><p>The Ninja Crispi is a glass air fryer that cooks everything from pasta to green beans and a whole lot in between. It reinvents the traditional air fryer whilst looking incredibly smart and almost futuristic for an everyday kitchen appliance. So, how can you possibly evolve the Ninja Crispi even further?</p><p>Well, Ninja has just launched the Ninja Crispi DualZone – the first glass air fryer to cook in <em>two independent zones</em>. You're telling me I can cook my salmon at the same time as my vegetables, and they'll both be cooked to perfection, ready to be plated at the same time? This is the feature I've been desperately hoping for! </p><div class="product"><a data-dimension112="6f14bb72-447e-468f-bebf-d472fd168e8b" data-action="Deal Block" data-label="The Ninja Crispi DualZone utilizes DualZone technology to cook two foods in two different ways while still managing to finish at the same time. With two independent 4 qt. containers totaling 8 qt. of cooking space and storage, it's impressive in features as well as in looks. Whilst it's available in four colors – Cyberspace, Bone, Vista Green and Rose Quartz – at the time of writing, Cyberspace was already sold out." data-dimension48="The Ninja Crispi DualZone utilizes DualZone technology to cook two foods in two different ways while still managing to finish at the same time. With two independent 4 qt. containers totaling 8 qt. of cooking space and storage, it's impressive in features as well as in looks. Whilst it's available in four colors – Cyberspace, Bone, Vista Green and Rose Quartz – at the time of writing, Cyberspace was already sold out." data-dimension25="$349.99" href="https://www.sharkninja.com/ninja-crispi-dualzone-glass-countertop-air-fryer-in-bone/DD101IV2.html?dwvar_DD101IV2_color=e7e6e1" 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="Si4Wg6RMvKV3sW6iU48gic" name="ninji_crispi_dualzone_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Si4Wg6RMvKV3sW6iU48gic.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ninja Crispi DualZone utilizes DualZone technology to cook two foods in two different ways while still managing to finish at the same time. With two independent 4 qt. containers totaling 8 qt. of cooking space and storage, it's impressive in features as well as in looks. Whilst it's available in four colors – Cyberspace, Bone, Vista Green and Rose Quartz – at the time of writing, Cyberspace was already sold out.<a class="view-deal button" href="https://www.sharkninja.com/ninja-crispi-dualzone-glass-countertop-air-fryer-in-bone/DD101IV2.html?dwvar_DD101IV2_color=e7e6e1" target="_blank" rel="nofollow" data-dimension112="6f14bb72-447e-468f-bebf-d472fd168e8b" data-action="Deal Block" data-label="The Ninja Crispi DualZone utilizes DualZone technology to cook two foods in two different ways while still managing to finish at the same time. With two independent 4 qt. containers totaling 8 qt. of cooking space and storage, it's impressive in features as well as in looks. Whilst it's available in four colors – Cyberspace, Bone, Vista Green and Rose Quartz – at the time of writing, Cyberspace was already sold out." data-dimension48="The Ninja Crispi DualZone utilizes DualZone technology to cook two foods in two different ways while still managing to finish at the same time. With two independent 4 qt. containers totaling 8 qt. of cooking space and storage, it's impressive in features as well as in looks. Whilst it's available in four colors – Cyberspace, Bone, Vista Green and Rose Quartz – at the time of writing, Cyberspace was already sold out." data-dimension25="$349.99">View Deal</a></p></div><h3 class="article-body__section" id="section-meet-the-ninja-crispi-dualzone"><span>Meet the Ninja Crispi DualZone</span></h3><div class="instagram-embed"><blockquote class="instagram-media"  data-instgrm-version="6" style="width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><p><a href="https://www.instagram.com/p/DZ3EkaCAjP5/" target="_blank">A post shared by Ninja Kitchen (@ninjakitchen)</a></p><p>A photo posted by  on </p></blockquote></div><p>The Ninja Crispi DualZone isn't the brand's first evolution of the Crispi; the Crispi Pro launched earlier this year. But, it is the first time it's used its patented DualZone technology on the innovative glass air fryer.</p><p>And it's the two independent cooking zones that are the real shining star of the new Crispi. Each zone is made up of a CleanCrisp glassware container that is designed for non-toxic cooking and speedy storage, but there's more technology at play here that's worth talking about.</p><p>Smart Finish, for example, is Ninja's answer to no-stress mealtimes. With this technology, the Ninja Crispi DualZone intelligently syncs both zones to finish at the same time. Pair this up with Match Cook that mirrors what settings you've got running across your zones, and you can use it for double-batch cooking, too.</p><p>No longer will you have to cook your food back-to-back with the Crispi or bring in other kitchen appliances to make sure your meal comes together at once – and for me, that sounds like the dream. And if you're keen to cook up using different functions, there are six customizable options onboard with Air Fry, Bake, Dehydrate, Max, Crisp, Roast and Recrisp all available,</p><h2 id="why-i-can-t-wait-to-try-it">Why I can't wait to try 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="2kHyui2n3sEdsvLSPqgy8c" name="ninja_crispi_dualzone_1" alt="The Ninja Crispi DualZone being used" src="https://cdn.mos.cms.futurecdn.net/2kHyui2n3sEdsvLSPqgy8c.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: Ninja)</span></figcaption></figure><p>I've loved the look of the Ninja Crispi since it launched, but I've already got the Ninja Foodi DualZone that allows me to cook on two sides at the same time. It's hard to compete with that until now. I finally feel like I could replace the Foodi with the Crispi, but there's more to it than that.</p><p>I've also recently moved over from plastic food storage containers to glass, and I've definitely noticed a difference when it comes to taste. If the same can be said for the Ninja Crispi DualZone, then I'm super intrigued. With no PFAS and PTFE, it also feels like a smart choice for chemical-free cuisine.</p><p>I'm really keen to grab a Ninja Crispi DualZone now that it's got the feature I've been desperately waiting for, and it doesn't hurt that it <em>looks</em> incredibly stylish, too. I'll reserve full judgment until we get it in for a full review. Now, I just need them to make a Ninja Crispi Pro DualZone, and I can fit food in for the whole family.</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-pro-review">Ninja Crispi Pro review: A meal prep marvel</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/ninja-crispi-release-first-look">Ninja just released an air fryer so portable you can take it to the office</a></li><li><a href="https://www.tomsguide.com/how-to/7-dangerous-mistakes-to-avoid-when-cooking-in-an-air-fryer">7 dangerous mistakes to avoid when cooking in an air fryer</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Save up to 50% on my favorite kitchen appliances for Prime Day — Ninja Slushi, Keurig, KitchenAid and more ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/save-up-to-50-percent-on-my-favorite-kitchen-appliances-for-prime-day-ninja-slushi-keurig-kitchenaid-and-more</link>
                                                                            <description>
                            <![CDATA[ Make life easier in the kitchen and save up to 50% on Ninja, KitchenAid and Instant Pot deals. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">C2vU7vpci8Ysoq485jeaoE</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/Sv2ETsRkio6ZEfXmMMvY3e-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 22 Jun 2026 12:35:34 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Kitchen &amp; 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/Sv2ETsRkio6ZEfXmMMvY3e-1280-80.jpg">
                                                            <media:credit><![CDATA[Ninja]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Ninja Slushi with frozen drinks ]]></media:description>                                                            <media:text><![CDATA[Ninja Slushi with frozen drinks ]]></media:text>
                                <media:title type="plain"><![CDATA[Ninja Slushi with frozen drinks ]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/Sv2ETsRkio6ZEfXmMMvY3e-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Prime Day is almost here, but you can pick up some great deals before the big day. I’ve been searching for the best savings on small kitchen appliances, and some of my favorites are on sale. </p><p>For those who love to bake, you can snag <a href="https://www.amazon.com/KitchenAid-KSM150PSPT-Artisan-Pouring-Shield/dp/B0000635XA/ref=sr_1_4?">KitchenAid’s Artisan stand mixer for $372, </a>which is 25% off. It's an outstanding performer and the top product I recommend in our<a href="https://www.tomsguide.com/best-picks/best-stand-mixers"> best stand mixer </a>buying guide. If you love multi-functional appliances, take a good look at Instant Pot's Duo Plus 9-in-1 Multicooker for just $69 — now half price.</p><p>Keep scrolling for more recommended Prime Day kitchen appliance deals! Don't forget to <a href="https://target.georiot.com/Proxy.ashx?tsid=45724&GR_URL=https%3A%2F%2Famazon.com%2Famazonprime%3Ftag%3Dftr-tomsguide-us-20%26ascsubtag%3Dtomsguide-us-7896170516091187610-20">sign up for a Prime membership</a> and stay tuned to our <a href="https://www.tomsguide.com/live/news/best-early-prime-day-deals-id-shop-2026-this-weekend">Prime Day deals live blog</a>. </p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><a href="https://www.amazon.com/primeday" target="_blank" rel="nofollow"><strong>shop all Prime Day deals</strong></a></li><li>Instant Pot DuoPlus  9-in-1 Multicooker:<strong> </strong><a href="https://www.amazon.com/Instant-Pot-Plus-60-Programmable/dp/B01NBKTPTS?" target="_blank" rel="nofollow"><strong>was $139 now $69</strong></a></li><li>Keurig K-Supreme Single Serve K-Cup Pod Coffee Maker: <a href="https://www.amazon.com/Keurig-MultiStream-Technology-Dual-Position-Customizable/dp/B0892TW82K/ref=sr_1_3?" target="_blank" rel="nofollow"><strong>was $189 now $99</strong></a></li><li>Ninja Slushi Frozen Drink and Slushie Machine with Rapid Chill:<strong> </strong><a href="https://www.amazon.com/Ninja-FS301-Professional-RapidChill-Technology/dp/B0D2LZYQ2M/ref=sr_1_4?th=1"><strong>was $349 now $199</strong></a></li><li>Ninja Foodi Smart XL 6-in1 Indoor Grill and Air Fryer (with probe): <a href="https://www.amazon.com/Ninja-FG551-Dehydrate-Thermometer-stainless/dp/B0B3S5GWX7/ref=sr_1_11?" target="_blank" rel="nofollow"><strong>was $299 now $199</strong></a></li><li>KitchenAid Artisan Series 5 Quart Tilt Head Stand Mixer:<a href="https://www.amazon.com/KitchenAid-KSM150PSPT-Artisan-Pouring-Shield/dp/B0000635XA/ref=sr_1_4?" target="_blank" rel="nofollow"><strong> was $499 now $372</strong></a></li></ul><h3 class="article-body__section" id="section-prime-day-small-kitchen-appliance-deals"><span>Prime Day Small Kitchen Appliance Deals</span></h3><div class="product"><a data-dimension112="171e9fdf-fbef-45a3-9c11-2ab5be39bc17" data-action="Deal Block" data-label="It may be too hot to cook a stew right now, but this Instant Pot has 9 functions and is recommended as the most versatile model in our buying guide. Steam, sauté, pressure cook, slow cook, keep warm, rice, sous vide, cake and yogurt — it's a small but mighty multifunctional appliance that’s gained a 4-star review. What’s more, with 50% off before Prime Day, it’s worth adding to your basket before the deal is over." data-dimension48="It may be too hot to cook a stew right now, but this Instant Pot has 9 functions and is recommended as the most versatile model in our buying guide. Steam, sauté, pressure cook, slow cook, keep warm, rice, sous vide, cake and yogurt — it's a small but mighty multifunctional appliance that’s gained a 4-star review. What’s more, with 50% off before Prime Day, it’s worth adding to your basket before the deal is over." data-dimension25="$69" href="https://www.amazon.com/Instant-Pot-Plus-60-Programmable/dp/B01NBKTPTS?t" 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="6PBS9SLa4Wzg9Hubqw4fwf" name="Instant Pot Duo Plus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/6PBS9SLa4Wzg9Hubqw4fwf.jpg" mos="" align="middle" fullscreen="" width="541" height="541" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>It may be too hot to cook a stew right now, but this Instant Pot has 9 functions and is recommended as the most versatile model in our buying guide. Steam, sauté, pressure cook, slow cook, keep warm, rice, sous vide, cake and yogurt — it's a small but mighty multifunctional appliance that’s gained a 4-star review. What’s more, with 50% off before Prime Day, it’s worth adding to your basket before the deal is over. <a class="view-deal button" href="https://www.amazon.com/Instant-Pot-Plus-60-Programmable/dp/B01NBKTPTS?t" target="_blank" rel="nofollow" data-dimension112="171e9fdf-fbef-45a3-9c11-2ab5be39bc17" data-action="Deal Block" data-label="It may be too hot to cook a stew right now, but this Instant Pot has 9 functions and is recommended as the most versatile model in our buying guide. Steam, sauté, pressure cook, slow cook, keep warm, rice, sous vide, cake and yogurt — it's a small but mighty multifunctional appliance that’s gained a 4-star review. What’s more, with 50% off before Prime Day, it’s worth adding to your basket before the deal is over." data-dimension48="It may be too hot to cook a stew right now, but this Instant Pot has 9 functions and is recommended as the most versatile model in our buying guide. Steam, sauté, pressure cook, slow cook, keep warm, rice, sous vide, cake and yogurt — it's a small but mighty multifunctional appliance that’s gained a 4-star review. What’s more, with 50% off before Prime Day, it’s worth adding to your basket before the deal is over." data-dimension25="$69">View Deal</a></p></div><div class="product"><a data-dimension112="3a948658-05d5-468e-a23f-589db1e23a2c" data-action="Deal Block" data-label="best coffee maker" data-dimension48="best coffee maker" data-dimension25="$99" href="https://www.amazon.com/Keurig-MultiStream-Technology-Dual-Position-Customizable/dp/B0892TW82K/ref=sr_1_3?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:528px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="5MQzmprW3phD6DAULApth9" name="Keurig K-Supreme Black Single-Serve K-Cup Pod Coffee" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/5MQzmprW3phD6DAULApth9.jpg" mos="" align="middle" fullscreen="" width="528" height="528" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you’re looking for a single-serve coffee maker, you can’t go wrong with a Keurig. What’s more, the K-Supreme single-pod coffee maker gets the thumbs-up from Tom's Guide and is voted as the best Keurig coffee maker in our <a href="https://www.tomsguide.com/home/coffee-makers/ive-tested-43-coffeemakers-in-two-years-but-id-only-spend-my-own-money-on-these-nine" data-dimension112="3a948658-05d5-468e-a23f-589db1e23a2c" data-action="Deal Block" data-label="best coffee maker" data-dimension48="best coffee maker" data-dimension25="$99">best coffee maker</a> buying guide. It features a streamlined design, user-friendly controls, and it doesn't skimp on flavor either.<a class="view-deal button" href="https://www.amazon.com/Keurig-MultiStream-Technology-Dual-Position-Customizable/dp/B0892TW82K/ref=sr_1_3?" target="_blank" rel="nofollow" data-dimension112="3a948658-05d5-468e-a23f-589db1e23a2c" data-action="Deal Block" data-label="best coffee maker" data-dimension48="best coffee maker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="a94f2380-6891-46b0-969a-57b479854b02" data-action="Deal Block" data-label="Ninja Slushi review" data-dimension48="Ninja Slushi review" data-dimension25="$199" href="https://www.amazon.com/Ninja-FS301-Professional-RapidChill-Technology/dp/B0D2LZYQ2M/ref=sr_1_4?th=1" 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="Md3V78E7M26tHBscxkVLck" name="ninja-slushi-frozen-drink--slushie-machi-bb8235b5-4eea-47de-86d3-333a308c683d.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Md3V78E7M26tHBscxkVLck.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Who isn't craving an ice-cold drink right now? Ninja's Slushi Frozen Drinks maker could just save you in a heatwave, and right now you can save 43%. What's more, we gave this super-fast slushie machine, which turns liquids into slush in 15 minutes (all thanks to Ninja's RapidChill technology), a 4-star rating in our <a href="https://www.tomsguide.com/home/home-appliances/ninja-slushi-review" data-dimension112="a94f2380-6891-46b0-969a-57b479854b02" data-action="Deal Block" data-label="Ninja Slushi review" data-dimension48="Ninja Slushi review" data-dimension25="$199">Ninja Slushi review</a>.<a class="view-deal button" href="https://www.amazon.com/Ninja-FS301-Professional-RapidChill-Technology/dp/B0D2LZYQ2M/ref=sr_1_4?th=1" target="_blank" rel="nofollow" data-dimension112="a94f2380-6891-46b0-969a-57b479854b02" data-action="Deal Block" data-label="Ninja Slushi review" data-dimension48="Ninja Slushi review" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="e7d2c4ec-2ed3-43ad-81f8-aa411b1cbf45" data-action="Deal Block" data-label="best stand mixer" data-dimension48="best stand mixer" data-dimension25="$372" href="https://www.amazon.com/KitchenAid-KSM150PSPT-Artisan-Pouring-Shield/dp/B0000635XA/ref=sr_1_4?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:545px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="E7YfdMSj8eyXG3hoMsbciK" name="KitchenAid Artisan pistashio" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/E7YfdMSj8eyXG3hoMsbciK.png" mos="" align="middle" fullscreen="" width="545" height="545" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>I just love the pistachio colorway of this KitchenAid stand mixer, which I recommend as the top-performing stand mixer in our <a href="https://www.tomsguide.com/best-picks/best-stand-mixers?utm_source=google&utm_medium=h5d&utm_campaign=h_tg_00284&gad_source=1&gad_campaignid=22851877377&gbraid=0AAAAAqzfqMYOt-1rvqrRxtkG9Qs098wEu&gclid=CjwKCAjwuuPRBhAnEiwA2Ji8eik-q-URBik-sD8yphL0tVNkXc-9yjwV1azwfig9e7Byd5wg2rQtGxoC5UEQAvD_BwE" data-dimension112="e7d2c4ec-2ed3-43ad-81f8-aa411b1cbf45" data-action="Deal Block" data-label="best stand mixer" data-dimension48="best stand mixer" data-dimension25="$372">best stand mixer</a> buying guide. It will withstand a whole load of baking, from mixing heavy dough to delicate sponges and the lightest of meringues. What’s more, once you buy this stand mixer, you won’t need to replace it for years. It comes with a 5-quart stainless steel bowl, flat beater, wire whisk and a pouring shield, and you can choose between 10 speeds. And if you’re not so keen on this colorway, there’s plenty more to choose from. <a class="view-deal button" href="https://www.amazon.com/KitchenAid-KSM150PSPT-Artisan-Pouring-Shield/dp/B0000635XA/ref=sr_1_4?" target="_blank" rel="nofollow" data-dimension112="e7d2c4ec-2ed3-43ad-81f8-aa411b1cbf45" data-action="Deal Block" data-label="best stand mixer" data-dimension48="best stand mixer" data-dimension25="$372">View Deal</a></p></div><div class="product"><a data-dimension112="ebe32e8c-0d76-4095-8693-b5e281cf05f9" data-action="Deal Block" data-label="Get ready for the 4th of July celebrations with the Ninja Foodi Smart XL 6-in-1 indoor air frying grill, the ultimate small appliance for your home, because it combines the functions of an air fryer, grill and oven all in one. It's also super easy to clean thanks to its ceramic-coated grills, and the added temperature probe will ensure yout protein is cooked to perfection." data-dimension48="Get ready for the 4th of July celebrations with the Ninja Foodi Smart XL 6-in-1 indoor air frying grill, the ultimate small appliance for your home, because it combines the functions of an air fryer, grill and oven all in one. It's also super easy to clean thanks to its ceramic-coated grills, and the added temperature probe will ensure yout protein is cooked to perfection." data-dimension25="$199" href="https://www.amazon.com/Ninja-FG551-Dehydrate-Thermometer-stainless/dp/B0B3S5GWX7/ref=sr_1_11?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:569px;"><p class="vanilla-image-block" style="padding-top:69.60%;"><img id="jJw4FJYqzaHhiTJetToQCS" name="Ninja Indoor Grill and air fryer" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jJw4FJYqzaHhiTJetToQCS.png" mos="" align="middle" fullscreen="" width="569" height="396" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Get ready for the 4th of July celebrations with the Ninja Foodi Smart XL 6-in-1 indoor air frying grill, the ultimate small appliance for your home, because it combines the functions of an air fryer, grill and oven all in one. It's also super easy to clean thanks to its ceramic-coated grills, and the added temperature probe will ensure yout protein is cooked to perfection.<a class="view-deal button" href="https://www.amazon.com/Ninja-FG551-Dehydrate-Thermometer-stainless/dp/B0B3S5GWX7/ref=sr_1_11?" target="_blank" rel="nofollow" data-dimension112="ebe32e8c-0d76-4095-8693-b5e281cf05f9" data-action="Deal Block" data-label="Get ready for the 4th of July celebrations with the Ninja Foodi Smart XL 6-in-1 indoor air frying grill, the ultimate small appliance for your home, because it combines the functions of an air fryer, grill and oven all in one. It's also super easy to clean thanks to its ceramic-coated grills, and the added temperature probe will ensure yout protein is cooked to perfection." data-dimension48="Get ready for the 4th of July celebrations with the Ninja Foodi Smart XL 6-in-1 indoor air frying grill, the ultimate small appliance for your home, because it combines the functions of an air fryer, grill and oven all in one. It's also super easy to clean thanks to its ceramic-coated grills, and the added temperature probe will ensure yout protein is cooked to perfection." data-dimension25="$199">View Deal</a></p></div><h2 id="home-appliances">HOME APPLIANCES</h2><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-home-appliance-deals"><span>We're tracking all the best Prime Day home appliance deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Home+Appliances&price=50_&min_discount_ratio=0.95&offer_type=all&rows=4&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.&show_countdown=true&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 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-right {      position: absolute;      right: 8px;      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-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right {      right: 0;      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-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">          \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.previousElementSibling.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-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>\n        \x3C/div>\n      \x3C/div>\n    \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">      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>    \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';             }          }                    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')}`                });              }            });          }        }        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';          }        }        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.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12;          } else {             this.displayLimit += (this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12;          }                    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 encodedTag = encodeURIComponent(this.activeDealTag.toLowerCase().replace(/\s+/g, '-'));             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');                    if (this.rowsSelect && this.rowsSelect.value) {            url.searchParams.append('rows', this.rowsSelect.value);          } else {             url.searchParams.append('rows', '12'); // default          }          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 = 12;          if (this.rowsSelect && this.rowsSelect.value) {            rowLimit = parseInt(this.rowsSelect.value, 10) || 12;          }          // 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 => {              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}`;                }              }              return `                \x3Ca href="${this.escapeHTML(linkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item">                  ${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);                }              });            });                                  } 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 = `              \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;                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let existingChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             if (this.getViewMode() === 'carousel') {                 if (!existingChevron) {                     gridWrapper.insertAdjacentHTML('beforeend', '\n                 \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.previousElementSibling.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>');                 }             } else {                 if (existingChevron) {                     existingChevron.remove();                 }             }          }                    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.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12);                 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');            if (!this.processArticleFired) {                  this.processArticleFired = true;                  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[ Moccamaster just dropped a new colorway exclusively for Amazon Prime Day — and it's effortlessly chic ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/coffee-makers/moccamaster-just-dropped-a-new-colorway-exclusively-for-amazon-prime-day-and-its-effortlessly-chic</link>
                                                                            <description>
                            <![CDATA[ One of our favorite drip coffee makers, the Technivorm Moccamaster KBGV Select, is now available in a color that's exclusive for Amazon Prime Day. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">JWsh5TkpivdT7RhBLnQh6J</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/UiHH9XBrF54zQfHBR5mfQB-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 22 Jun 2026 07:31:10 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Coffee Makers]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Kitchen &amp; Dining]]></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/UiHH9XBrF54zQfHBR5mfQB-1280-80.jpg">
                                                            <media:credit><![CDATA[Technivorm-Moccamaster]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Technivorm Moccamaster in Eucalpytus in a kitchen]]></media:description>                                                            <media:text><![CDATA[The Technivorm Moccamaster in Eucalpytus in a kitchen]]></media:text>
                                <media:title type="plain"><![CDATA[The Technivorm Moccamaster in Eucalpytus in a kitchen]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/UiHH9XBrF54zQfHBR5mfQB-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It's not unusual for the <a href="https://www.tomsguide.com/reviews/technivorm-moccamaster-kbgv-select">Technivorm Moccamaster KBGV Select</a> to be released in a new color. Earlier this year, the Dutch brand announced its Color of the Year with the <a href="https://www.tomsguide.com/home/coffee-makers/moccamaster-just-launched-a-new-colorway-for-2026-and-it-might-be-my-favorite-yet">Moccamaster in Sorbet</a>. And there's plenty more where that came from with the Moccamaster available in a wide selection of colors all-year round.</p><p>What <em>is</em> a little unusual about this latest color drop is that it's launched for one reason in particular — Amazon Prime Day. Holding the overall top spot amongst the <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers">best coffee makers</a> we've ever tested, I'm excited to see it in this new color even if the circumstances are unexpected.</p><p>So, to mark Amazon Prime Day 2026, Moccamaster has dropped this exclusive color for the annual sales event — Eucalyptus. And, for a little something extra special, it's offering a 33 per cent discount on the usual price during the sales period (23 to 26 June).</p><div class="product"><a data-dimension112="52f5fc12-6c53-4273-80cb-ba51261ec3f9" data-action="Deal Block" data-label="Technivorm Moccamaster KBGV Select" data-dimension48="Technivorm Moccamaster KBGV Select" data-dimension25="$369" href="https://www.amazon.com/dp/B0GV1HHTY8" 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="2oqhg9qDyxHmbWeLyRUZsd" name="moccamaster-eucalpytus-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/2oqhg9qDyxHmbWeLyRUZsd.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you want a coffee maker-slash-design statement, the <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review" data-dimension112="52f5fc12-6c53-4273-80cb-ba51261ec3f9" data-action="Deal Block" data-label="Technivorm Moccamaster KBGV Select" data-dimension48="Technivorm Moccamaster KBGV Select" data-dimension25="$369">Technivorm Moccamaster KBGV Select</a> won't do you far wrong. Not only is it SCA-approved, it's got great features for a drip brwer and also looks incredibly stylish. $369 is quite expensive for a drip coffee maker, but this Amazon Prime Day discount certainly softens the blow.<a class="view-deal button" href="https://www.amazon.com/dp/B0GV1HHTY8" target="_blank" rel="nofollow" data-dimension112="52f5fc12-6c53-4273-80cb-ba51261ec3f9" data-action="Deal Block" data-label="Technivorm Moccamaster KBGV Select" data-dimension48="Technivorm Moccamaster KBGV Select" data-dimension25="$369">View Deal</a></p></div><h2 id="why-i-ll-be-picking-one-up">Why I'll be picking one up</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:1548px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="X6nMVTQCHFTnfzNKPUTfQB" name="moccamaster_primeday_2" alt="The Technivorm Moccamaster in Eucalpytus in a kitchen" src="https://cdn.mos.cms.futurecdn.net/v2/t:207,l:15,cw:1548,ch:870,q:80/X6nMVTQCHFTnfzNKPUTfQB.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: Technivorm-Moccamaster)</span></figcaption></figure><p>While the Prime Day exclusive color is certainly alluring, it's not just the Eucalyptus hue that is swaying my decision to grab one. It's mainly down to the Technivorm Moccamaster KBGV Select's impressive features.</p><p>Not only did it secure a <a href="https://www.tomsguide.com/home/coffee-makers/technivorm-moccamaster-kgbv-select-review">four-and-a-half star review</a> from our testers, but it's held a spot in our <a href="https://www.tomsguide.com/home/coffee-makers/best-coffee-makers">coffee maker buying guide</a> for five years now. We even recently revisited our review in 2026 to check if it was still the cream of the crop — and we ruled it most certainly is.</p><p>Handmade in the Netherlands, it boasts quality construction, dependable results and a sleek design. In fact, our reviewer Erin (who runs the <a href="https://www.tomsguide.com/tag/the-coffee-lab">Tom's Guide Coffee Lab</a>) stated: "The Technivorm Moccamaster KGBV Select has stood the test of time, and for good reason. This is widely regarded as the best drip coffee maker ever invented. With SCA-approved functionality and a 5-year warranty (+lifetime repairs), I recommend the Moccamaster with every fiber of my coffee-addled being."</p><p>And new color releases bring a new aesthetic that may just make this machine even more appealing. Sure, Juniper and Pistachio are already available if you're looking for shades of green, but there's something about Eucalyptus that has really caught my eye.</p><h2 id="availability">Availability</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:1713px;"><p class="vanilla-image-block" style="padding-top:56.22%;"><img id="srighRwsqVs6QmL2L3FdQB" name="moccamaster_primeday_1" alt="The Technivorm Moccamaster in Eucalpytus in a kitchen" src="https://cdn.mos.cms.futurecdn.net/v2/t:0,l:0,cw:1713,ch:963,q:80/srighRwsqVs6QmL2L3FdQB.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="caption-text">A splash of color </span><span class="credit" itemprop="copyrightHolder">(Image credit: Technivorm-Moccamaster)</span></figcaption></figure><p>While the Moccamaster in Eucalpytus is an exclusive launch for Prime Day — running 23 to 26 June with a 33 per cent off limited time deal — it won't be the last time you see it. In fact, I've been told that it'll return in the fall, which is great news for anyone eager for a new coffee maker but not quite ready to invest.</p><p>And despite this being an Amazon Prime Day exclusive, you can also pick it up at other retailers like Williams Sonoma and Crate & Barrel. </p><ul><li><a href="https://www.tomsguide.com/how-to/7-ways-to-create-a-barista-style-coffee-bar-in-your-kitchen">7 ways to create a barista-style coffee bar in your kitchen</a></li><li><a href="https://www.tomsguide.com/home/coffee-doesnt-have-to-be-an-expensive-hobby-heres-my-favorite-cheap-accessories">Coffee doesn’t have to be an expensive hobby — here’s my 5 favorite cheap accessories</a></li><li><a href="https://www.tomsguide.com/home/how-to-make-coffee-without-a-coffee-maker">How to make coffee without a coffee maker — 5 easy techniques</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ 5 signs your plant needs a bigger pot right now ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/5-signs-your-plant-needs-a-bigger-pot-right-now</link>
                                                                            <description>
                            <![CDATA[ Learn the most common houseplant care mistakes and simple fixes that keep plants thriving. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">CKgFGLrx5oMvrHhVbjmXAe</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/pg7DcTraQP5Buiw6ghjPFW-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 20 Jun 2026 09:15:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                                    <dc:creator><![CDATA[ Alice Carroll ]]></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/pg7DcTraQP5Buiw6ghjPFW-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                        <media:description><![CDATA[A range of houseplants lined up on a shelf]]></media:description>                                                            <media:text><![CDATA[Repotting snake plant]]></media:text>
                                <media:title type="plain"><![CDATA[Repotting snake plant]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/pg7DcTraQP5Buiw6ghjPFW-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If some of your potted plants aren’t looking so good and you think you’ve tried everything, they may be root-bound. This stealthy condition often mimics under-watering or nutrient deficiencies, leaving many well-meaning plant parents completely baffled. </p><p>It happens quietly beneath the soil line, slowly choking off the plant's lifelines until intervention becomes absolutely critical. ‘Root-bound’ simply means the plant has outgrown its container. Its roots have filled all of the space and formed a dense, tangled mass. To learn the signs of a root-bound plant and how to save it, read on.</p><section class="howto-block">                    <h3>1. Visible roots </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/7jb7oc4bJDRpAwM2ogjcYZ.jpg"                                        alt="Repotting aloe vera plant "                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/7jb7oc4bJDRpAwM2ogjcYZ.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Can you see roots emerging from the drainage holes at the bottom of the pot or from the soil on the surface? Visible roots are the most obvious sign that a plant’s roots need more space and are actively searching for a way out. </p><p>When roots begin wrapping tightly around the upper rim of the soil, the plant is practically begging for a larger home.</p></p>                </section><section class="howto-block">                    <h3>2. Soil drying quickly </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/Usz7J2Fa5XZPmD8kJuDHcG.jpg"                                        alt="Watering a peace lily"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/Usz7J2Fa5XZPmD8kJuDHcG.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>If you discover that you’re having to water a plant more often, that you’re finding the topsoil dry earlier than usual, that’s probably because the roots have taken up so much space that there’s less soil to absorb moisture.</p><p>Without enough soil acting as a sponge, the water quickly evaporates. You might find yourself watering every couple of days instead of your usual weekly routine just to keep the plant upright.</p></p>                </section><section class="howto-block">                    <h3>3. Leaves turning yellow </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/SUzgdvH3LFnTVxs6kPtYKd.png"                                        alt="Overwatered monstera houseplant"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/SUzgdvH3LFnTVxs6kPtYKd.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p> If the foliage wilts soon after watering, or leaves are yellowing and falling off, that’s because the roots can no longer draw sufficient nutrients from the soil. The packed roots are limiting what moisture and nutrients the soil can provide; the roots need more space and soil to thrive. </p><p>This structural starvation often starts with the oldest, lowest leaves losing their color first as the plant prioritizes survival.</p></p>                </section><section class="howto-block">                    <h3>4. Water running straight through </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/gW4NxFr3Jbp3xwJTPWBXzE.jpg"                                        alt="Repotting a peace lily"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/gW4NxFr3Jbp3xwJTPWBXzE.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>When you water your plant, does the water seem to go through the soil and straight out the drainage holes? That’s another sign that the roots are occupying too much space, creating a solid block that forces water to slide right down the inside edges of the pot instead of soaking in. </p><p>If the drainage saucer fills up instantly while the actual root ball stays bone-dry, you have a classic root-bound situation.</p></p>                </section><section class="howto-block">                    <h3>5. Poor growth </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/mHfbHxSMppxYdpVHPqpyEc.jpg"                                        alt="Yellow leaf on orchid"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/mHfbHxSMppxYdpVHPqpyEc.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Have you noticed that new foliage isn’t growing or the growth is smaller than usual? That’s another sign that your plant is root-bound and not obtaining the nutrients it needs to thrive during its peak growing season. </p><p>Without room to expand below, the plant simply lacks the biological energy required to push out impressive new leaves above.</p></p>                </section><section class="howto-block">                    <h3>How to salvage your root-bound plant </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/SJDUVhxLTgCjB8dWkCKzng.jpg"                                        alt="Woman repotting a houseplant"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/SJDUVhxLTgCjB8dWkCKzng.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Carefully ease the plant from its pot by supporting the base of the main stem. <strong>Squeeze the plastic sides or run a blunt knife around the inner rim</strong> of rigid containers to break the suction. </p><p>Next,<strong> examine the roots and gently tease them apart with your fingers</strong> so they can grow outward. If they are completely compacted, <strong>use sterile shears to make a few shallow, vertical cuts and trim away dead or circling root</strong>s — up to 25% of the total mass.</p><p><strong>Move the plant into a new container that is one to two inches larger </strong>in diameter and features good drainage holes. <strong>Settle it on a layer of fresh potting mix</strong> at its original soil level, f<strong>ill the sides, and press down gently to remove air pockets</strong>. </p><p>Finish by <strong>watering thoroughly to settle the soil, placing the pot in bright, indirect light, and waiting 2 to 4 weeks before fertilizing </strong>so the roots can safely recover.</p></p>                </section><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Wwz8ae"></div>                            </div>                            <script src="https://kwizly.com/embed/Wwz8ae.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/home/stop-misting-your-succulents-its-drying-them-out">Your succulent is dying of thirst every time you mist it — do this instead</a></li><li><a href="https://www.tomsguide.com/home/how-to-revive-a-dying-houseplant">How to revive a dying houseplant — 7 steps that actually work</a></li><li><a href="https://www.tomsguide.com/home/houseplants-taking-over-heres-how-to-repot-a-swiss-cheese-plant">Houseplants taking over? Here's how to repot a Swiss cheese plant</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ This affordable hack promises streak-free windows using two very unusual items — here's what happened ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/this-affordable-hack-promises-streak-free-windows-using-two-very-unusual-items-heres-what-happened</link>
                                                                            <description>
                            <![CDATA[ Cleaning your windows can be such a chore, especially if you put in all the effort and still end up with streaks. This unusual hack promises to solve all those problems – and it's also super affordable. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">w9cRE5fF5y3sjEpqQgAijb</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/hMea88CHX4khzB4wngig9J-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 19 Jun 2026 09:12:36 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></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/hMea88CHX4khzB4wngig9J-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Using newspaper and vinegar to clean a window]]></media:description>                                                            <media:text><![CDATA[Using newspaper and vinegar to clean a window]]></media:text>
                                <media:title type="plain"><![CDATA[Using newspaper and vinegar to clean a window]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/hMea88CHX4khzB4wngig9J-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Like cleaning my car, cleaning my windows seems like a chore around the house that <em>always</em> sets me up for failure. It doesn't seem to matter how much I scrub and buff, I constantly end up with soapy streaks staring back at me. Sometimes, I wish I'd just left my windows as they were. </p><p>While I've tried following advice on <a href="https://www.tomsguide.com/how-to/how-to-clean-windows">how to clean windows and leave them streak-free</a>, at this point I'm inclined to just blame my cleaning products and give up. That's easier than blaming my own cleaning skills, right? </p><p>Instead of throwing in the towel though, I find myself constantly on the hunt for hacks that promise a streak-free solution and I'm hoping this expert hack will be the answer. Not only is it affordable, but it uses two unusual household items I already have in my home. Costing nothing to try, I thought I'd give it a go and I was surprised by the results.</p><h2 id="how-does-it-work-4">How does it 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vYk58URz9yS4qf2H5AikAJ" name="newspaper_hack_3" alt="Using newspaper and vinegar to clean a window" src="https://cdn.mos.cms.futurecdn.net/vYk58URz9yS4qf2H5AikAJ.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>Allan Reid, uPVC specialist and founder of <a href="https://artwindowsanddoors.co.uk/windows/" target="_blank" rel="nofollow">Art Windows and Doors</a> knows a lot about windows. He also advocates for a really simple cleaning technique that I'd never heard about before and it uses two items – white vinegar and newspaper.</p><p>It sounds like something you'd try out when there weren't any proper cleaning products available, rather than something you'd opt for <em>instead</em> of cleaning products. </p><p>But, Reid says it's worth trying: "It does sound a little strange if you've never tried it before. But it works surprisingly well. You don't get those annoying bits of fluff left behind, and it helps leave the glass looking really clear."</p><p>It's really cheap and easy to do, following these three simple steps:</p><ul><li><strong>Mix equal parts water and white vinegar in a bottle</strong></li><li><strong>Spray it on windows</strong></li><li><strong>Quickly scrub it with scrunched up newspaper</strong></li></ul><p>Reid adds: "Sometimes we convince ourselves we need loads of products for every little job around the house. But this is one of those simple tricks that's stuck around because it genuinely works." </p><p>With that bold statement in mind, I thought it was only fair to give it a try and see if I can finally get rid of those streaks that seem to appear as soon as I think I've done a good job.</p><div class="product"><a data-dimension112="1c9c8951-38b0-4fc4-9f9d-e36e6829f168" data-action="Deal Block" data-label="Mix up your white vinegar and water solution in a spray bottle so you can spritz it directly onto your windows. These bottles are the perfect size for little jobs around the house, too, so you can utilize them in more ways than one." data-dimension48="Mix up your white vinegar and water solution in a spray bottle so you can spritz it directly onto your windows. These bottles are the perfect size for little jobs around the house, too, so you can utilize them in more ways than one." data-dimension25="$6.97" href="https://www.amazon.com/Spraying-Cleaning-Solutions-Essential-Correction/dp/B0DCJGV4ML" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1562px;"><p class="vanilla-image-block" style="padding-top:85.53%;"><img id="H54eBpMC35ZFatHWijJhfF" name="cleaning_spray_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/H54eBpMC35ZFatHWijJhfF.png" mos="" align="middle" fullscreen="" width="1562" height="1336" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Mix up your white vinegar and water solution in a spray bottle so you can spritz it directly onto your windows. These bottles are the perfect size for little jobs around the house, too, so you can utilize them in more ways than one.<a class="view-deal button" href="https://www.amazon.com/Spraying-Cleaning-Solutions-Essential-Correction/dp/B0DCJGV4ML" target="_blank" rel="nofollow" data-dimension112="1c9c8951-38b0-4fc4-9f9d-e36e6829f168" data-action="Deal Block" data-label="Mix up your white vinegar and water solution in a spray bottle so you can spritz it directly onto your windows. These bottles are the perfect size for little jobs around the house, too, so you can utilize them in more ways than one." data-dimension48="Mix up your white vinegar and water solution in a spray bottle so you can spritz it directly onto your windows. These bottles are the perfect size for little jobs around the house, too, so you can utilize them in more ways than one." data-dimension25="$6.97">View Deal</a></p></div><h2 id="here-s-what-happened-when-i-tried-it-4">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="xHXEDhqyc4mTSJvgCdUqwH" name="newspaper_hack_1" alt="Using newspaper and vinegar to clean a window" src="https://cdn.mos.cms.futurecdn.net/xHXEDhqyc4mTSJvgCdUqwH.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>My windows get so messy, it's almost a bit embarrassing. With two little kids around my house, there's sticky fingerprints and strange streaks that go unaccounted for. And as summer hits, the sunshine is producing a very unforgiving gleam to my windows showing up all the dust and dirt. So, I was more than happy to give this hack a try.</p><p>With my white vinegar and water solution in hand, I spritzed the solution across one of my windows just to try it out. Then, I quickly grabbed my crumpled newspaper to give it a good scrub. I was initially worried that the newspaper print would just smear all over the window, but I'm happy to report that wasn't the case.</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="XyKZCJ9iBBxvQLjhGxR26J" name="newspaper_hack_2" alt="Using newspaper and vinegar to clean a window" src="https://cdn.mos.cms.futurecdn.net/XyKZCJ9iBBxvQLjhGxR26J.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>My initial thoughts were that it looked surprisingly good. But, I needed to give it a bit of time to sit before I gave it my full verdict. As you may have witness yourself, for some reason streaks decide to show up as if by magic after they've dried. I was worried this time that they were just hiding from me. Turns out, they weren't. </p><p>When I returned 30 minutes later, my window was clean and streak-free. It didn't cost me a dollar to try out, though if you need to buy the two items, they are pretty cheap. It's definitely something I feel confident enough to try on a few more windows and maybe, finally, I can stop buying cleaning products hoping it'll solve the problem.</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-employed-myself-to-spring-clean-my-home-and-it-might-just-be-the-perfect-motivation-hack">I 'employed myself' to give my home its spring clean — and it turned out to be the perfect motivation hack</a></li><li><a href="https://www.tomsguide.com/home/i-cleaned-my-house-using-only-homemade-cleaning-products-for-a-week-heres-what-happened">I cleaned my house using only homemade cleaning products for a week — here's what happened</a></li><li><a href="https://www.tomsguide.com/home/a-professional-cleaner-just-revealed-why-your-dishwasher-cleaning-tablet-isnt-working-and-it-makes-total-sense">This viral TikTok cleaning hack just revealed why your dishwasher tablet isn't working — and it makes total sense</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ This is what's really causing your tomato leaves to curl — and how to actually fix it ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/this-is-whats-really-causing-your-tomato-leaves-to-curl-and-how-to-actually-fix-it</link>
                                                                            <description>
                            <![CDATA[ Learn what causes tomato leaves to curl and how to restore your plants to health. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">tgcyvXDWegXG4E6diRLHiT</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/dhiQPnNqEPpgVKEGWHDndT-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 19 Jun 2026 06:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                <author><![CDATA[ kaycee.hill@futurenet.com (Kaycee Hill) ]]></author>                    <dc:creator><![CDATA[ Kaycee Hill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xHn6RmpEqg87cvtLwrBu9G.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/dhiQPnNqEPpgVKEGWHDndT-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Ripening tomato plant with curled leaves]]></media:description>                                                            <media:text><![CDATA[Ripening tomato plant with curled leaves]]></media:text>
                                <media:title type="plain"><![CDATA[Ripening tomato plant with curled leaves]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/dhiQPnNqEPpgVKEGWHDndT-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Curling tomato leaves are your plant's way of telling you something is wrong. When you notice the foliage starting to curl, roll, or twist, it's tempting to panic. But understanding what causes tomato leaves to curl is crucial because different problems require different solutions. </p><p>Some causes resolve on their own once conditions improve. Others demand immediate action. Here are the five most common reasons your tomato plants are curling and exactly what to do about each one. </p><section class="howto-block">                    <h3>1. Heat stress strikes first</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/Sjmn35uGYyzGmrbEi64zFH.jpg"                                        alt="Growing tomato plants with curled leaves"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/Sjmn35uGYyzGmrbEi64zFH.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Hot, dry conditions are the most common culprit behind curling tomato leaves. When temperatures spike and moisture becomes scarce, tomato plants face an impossible choice: they're losing water through evaporation faster than their roots can absorb it. </p><p>The plant responds by curling leaf margins upward — a survival strategy that reduces sun exposure and water loss. You'll typically notice this on lower leaves first.</p><p>To fix this, water deeply and consistently.<strong> Aim for 1 inch of water per week delivered directly to the root zone with a drip line or soaker hose rather than overhead watering</strong>, which spreads disease. </p><p>You can also <strong>add a 2-inch mulch layer around the base to slow soil moisture evaporation</strong>. Once conditions cool or moisture returns, the curling typically resolves within days. The good news is that temporary leaf curl from heat stress rarely impacts your final harvest or plant growth long-term.</p></p>                </section><section class="howto-block">                    <h3>2. When you prune too much </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/UFB2o7HwSJud7AywrZAb7A.jpg"                                        alt="Curling tomato plant"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/UFB2o7HwSJud7AywrZAb7A.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Pruning tomato plants encourages fruit production, but removing too much foliage at once creates shock. When you strip away energy-generating leaves suddenly, the remaining foliage curls as the plant struggles to cope with the stress and loss of photosynthetic capacity.</p><p>The remedy is straightforward: <strong>water thoroughly and wait</strong>. The plant needs time to recover and grow new leaves. Within a couple of weeks, new foliage should emerge and the curling should disappear. </p><p>In the future, <strong>prune conservatively and gradually </strong>rather than removing large sections of growth at once. Focus on removing only lower leaves and suckers between main stems.</p></p>                </section><section class="howto-block">                    <h3> 3. Transplant trauma</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/oMJydkdU4C9Ti6H2gqTsCJ.jpg"                                        alt="Watering seedlings by window"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/oMJydkdU4C9Ti6H2gqTsCJ.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Moving seedlings into the garden disrupts roots and exposes plants to temperature fluctuations. Some tomato varieties respond by curling their leaves as they acclimate to outdoor conditions. This stress is temporary but worth preventing when possible.</p><p>If your transplanted tomatoes already show curling leaves, <strong>increase watering to help them settle in</strong>. Most plants recover naturally within two weeks without intervention. </p><p>To minimize transplant shock in future seasons,<strong> harden off seedlings by gradually exposing them to outdoor conditions before planting</strong>. Transplant on a cooler, overcast day rather than in full sun, and <strong>handle root balls gently</strong>. </p><p>Temporary shade from a cloth or tarp for the first few days also reduces stress significantly.</p></p>                </section><section class="howto-block">                    <h3>4. Viruses look different than stress</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/LThGCzgFA47PJxzRWqcBo3.jpg"                                        alt="Early tomato blight showing on tomato plant leaves"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/LThGCzgFA47PJxzRWqcBo3.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Tomato blight appears as brown or dark spots on leaves that rapidly expand and kill foliage. Early blight shows up after plants have set fruit and appears as concentric rings on lower leaves first. </p><p>Late blight is more severe. It spreads quickly in cool, wet conditions and can destroy an entire plant in days. Blight-infected leaves curl, yellow, and drop, leaving fruit exposed to sunscald.</p><p>Blight is a fungal disease spread by water splash and poor air circulation. Once you see it, act immediately.<strong> Remove all infected leaves and dispose of them in the trash —never compost blight-infected material</strong>. To improve airflow around the base of the plant, you can prune lower branches. </p><p>If blight continues spreading despite these steps, <strong>remove the entire plant</strong> to prevent it from infecting neighboring tomatoes.</p><p>Prevention is far more effective than treatment. <strong>Choose blight-resistant varieties like Mountain Magic F1 or Artemis F1 when planting</strong>. Space plants far enough apart for air circulation, mulch to prevent soil splash, and remove lower leaves as the plant grows. These practices eliminate the humid conditions blight needs to thrive.</p></p>                </section><section class="howto-block">                    <h3>5. Chemical drift is irreversible</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/sQLgTPm98ScYywUxY37Vea.jpg"                                        alt="Curled tomato plant leaves "                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/sQLgTPm98ScYywUxY37Vea.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Weed killer drifting from nearby lawn treatments can destroy tomato foliage. When herbicides like 2,4-D or dicamba blow onto your plants, they cause leaves to bend downward with individual leaflets curling upward in a cup-like pattern. This looks distinctly different from heat-stress curling. </p><p>Another sneaky source is contaminated compost or mulch containing persistent herbicides like picloram or clopyralid, which are chemicals that linger in soil for years.</p><p>Unfortunately, there's no cure for herbicide injury. You're left with a "wait and see" approach. Some plants recover and produce fruit despite the damage. Others decline and fail to fruit. Prevention is your only real defense. <strong>Keep weed killers away from tomato areas entirely</strong>.<strong> </strong></p></p>                </section><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XZQZ5O"></div>                            </div>                            <script src="https://kwizly.com/embed/XZQZ5O.js" async></script><figure class="van-image-figure pull-right inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:676px;"><p class="vanilla-image-block" style="padding-top:31.51%;"><img id="ZCwuUFRE3YeboHR4zyEWmb" name="r3t8zZ5ve4GewFTeoCM3R6" alt="Google" src="https://cdn.mos.cms.futurecdn.net/ZCwuUFRE3YeboHR4zyEWmb.jpg" mos="" align="right" fullscreen="" width="676" height="213" attribution="" endorsement="" class="pull-rightinline"></p></div></div><figcaption itemprop="caption description" class="pull-right inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></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/gardening/why-are-my-tomato-plant-leaves-turning-yellow-5-steps-to-save-your-harvest-clone">Double your tomato harvest with this 3-step pruning system</a></li><li><a href="https://www.tomsguide.com/home/gardening/why-are-my-tomato-plant-leaves-turning-yellow-5-steps-to-save-your-harvest">Why are my tomato plant leaves turning yellow? 5 steps to save your harvest</a></li><li><a href="https://www.tomsguide.com/home/gardening/why-marigolds-are-the-best-companion-flower-for-a-pest-free-vegetable-garden-especially-tomatoes">Why marigolds are the best companion flower for a pest-free vegetable garden (especially tomatoes)</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Panasonic just released a new microwave that drops the one feature we've all been waiting for ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/panasonic-just-released-a-new-microwave-that-drops-the-one-feature-weve-all-been-waiting-for</link>
                                                                            <description>
                            <![CDATA[ The Panasonic Japanese Microwave has just arrived, revolutionizing the humble act of heating up leftovers. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">yrbvtp7jci5r3fAueTnqoX</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/TUj5TrbnyyzK2V68eJsN6a-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 17 Jun 2026 11:00:59 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></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/TUj5TrbnyyzK2V68eJsN6a-1280-80.jpg">
                                                            <media:credit><![CDATA[Panasonic]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Panasonic Japanese Microwave on a countertop]]></media:description>                                                            <media:text><![CDATA[The Panasonic Japanese Microwave on a countertop]]></media:text>
                                <media:title type="plain"><![CDATA[The Panasonic Japanese Microwave on a countertop]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/TUj5TrbnyyzK2V68eJsN6a-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Panasonic has been releasing innovative kitchen appliances for decades. Evolving with the times and listening to consumer concerns, the Japanese company has just launched a <a href="https://www.tomsguide.com/us/best-microwaves,review-6316.html">microwave </a>that comes with a feature many of us desperately want from our microwaves: even cooking.</p><p>I love heating up leftovers. "Cook once, eat thrice" is a motto I live by, but I don't <em>love</em> that my current microwave is pretty uneven with its spread of heat. Some parts are piping hot while others are unappealingly cold. Finally, Panasonic has an answer in the form of the Japanese Microwave.</p><p>Launching for <a href="https://shop.panasonic.com/products/1-cu-ft-japanese-microwave-with-genuis-sensor-2-0-inverter-technology-12000w-nn-sf57rm" target="_blank" rel="nofollow">$429.95 at Panasonic</a>, the Japanese Microwave utilizes smart technology to revolutionize the way you cook up your food. And it also looks <em>really</em> nice. Here's what's packed into this modern-looking microwave.</p><div class="product"><a data-dimension112="52f5fc12-6c53-4273-80cb-ba51261ec3f9" data-action="Deal Block" data-label="The Panasonic Japanese Microwave brings Japanese innovation to your countertop. Combining a sleek design with smart technology, it's a microwave that promises easy, even cooking, reheating, and defrosting." data-dimension48="The Panasonic Japanese Microwave brings Japanese innovation to your countertop. Combining a sleek design with smart technology, it's a microwave that promises easy, even cooking, reheating, and defrosting." data-dimension25="$429" href="https://shop.panasonic.com/products/1-cu-ft-japanese-microwave-with-genuis-sensor-2-0-inverter-technology-12000w-nn-sf57rm" 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="gUy7WR9jYs9YfV6nFuyd4e" name="panasonic_japanese_microwave_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/gUy7WR9jYs9YfV6nFuyd4e.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Panasonic Japanese Microwave brings Japanese innovation to your countertop. Combining a sleek design with smart technology, it's a microwave that promises easy, even cooking, reheating, and defrosting.<a class="view-deal button" href="https://shop.panasonic.com/products/1-cu-ft-japanese-microwave-with-genuis-sensor-2-0-inverter-technology-12000w-nn-sf57rm" target="_blank" rel="nofollow" data-dimension112="52f5fc12-6c53-4273-80cb-ba51261ec3f9" data-action="Deal Block" data-label="The Panasonic Japanese Microwave brings Japanese innovation to your countertop. Combining a sleek design with smart technology, it's a microwave that promises easy, even cooking, reheating, and defrosting." data-dimension48="The Panasonic Japanese Microwave brings Japanese innovation to your countertop. Combining a sleek design with smart technology, it's a microwave that promises easy, even cooking, reheating, and defrosting." data-dimension25="$429">View Deal</a></p></div><h2 id="even-cooking-with-smart-technology">Even cooking with smart technology</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="7qtrP7qKZvSuSrrbNqZ96a" name="panasonic_microwave_2" alt="A woman places her leftovers into the Panasonic Japanese Microwave" src="https://cdn.mos.cms.futurecdn.net/7qtrP7qKZvSuSrrbNqZ96a.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: Panasonic)</span></figcaption></figure><p>As mentioned, the Panasonic Japanese Microwave comes with a feature we've all been desperately waiting for – even cooking for your meals. To achieve this, Panasonic has loaded up its new microwave with some super smart tech that I'm pretty excited about.</p><p>The Genius Sensor 2.0 is one of the shining features that makes this possible, precisely monitoring the temperature of your food for even heating. This is paired up with some pretty clever 3D antenna technology to target your food at a whole range of angles, meaning that those cold spots you hate to find in your leftovers will be <em>gone</em>.</p><p><a href="https://na.panasonic.com/news/panasonic-announces-the-new-japanese-microwave-designed-for-modern-american-kitchens" target="_blank" rel="nofollow">Panasonic</a> reveals that the Genius Sensor 2.0 uses a thermo sensor positioned at 64 points in the microwave interior: "This allows the microwave to respond to different foods and temperatures as they cook, helping deliver more even results across the entire plate, all with a simple push of a button."</p><p>And using Panasonic's patented Inverter Technology, the Japanese Microwave will maintain a consistent power level rather than pulsing on and off to reduce those hot and cold spots even further.</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="i37HymgkiE8QqYfUvkdUyZ" name="panasonic_microwave_3" alt="Pressing the one-touch button on the Panasonic Japanese Microwave" src="https://cdn.mos.cms.futurecdn.net/i37HymgkiE8QqYfUvkdUyZ.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: Panasonic)</span></figcaption></figure><p>Panasonic's one and done approach works in a number of ways from the One Push Reheat button that'll bring your food to the ideal temperature with just one press of a button. </p><p>To One Bowl Cooking where you can add all the fresh ingredients for a dish into a single bowl and the Japanese Microwave will cook them all up throughly and evenly. The options include Chili, Carbonara, and more.</p><h2 id="ridiculously-good-looking">Ridiculously good looking</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="rDmBUSQBCv6wSN5Lw4jorn" name="panasonic_microwave_4" alt="The Panasonic Japanese Microwave on a countertop next to a sink" src="https://cdn.mos.cms.futurecdn.net/rDmBUSQBCv6wSN5Lw4jorn.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: Panasonic)</span></figcaption></figure><p>For this microwave, Panasonic has also focused on style, adopting the design-first approach that has long been popular in Japan. And I'll be honest, I've never looked at a microwave and thought it was a pretty sleek addition to my kitchen. With the Japanese Microwave, my mind has been changed.</p><p>Not only am I impressed by its minimalistic and smart design, but I'm particularly interested in its pull-down, oven-style door to fit into tighter kitchen spaces. It's a design feature makes this microwave smoother, quieter and better looking.</p><p>You've also got a flatbed interior design that's modern and easier to clean with the Japanese Microwave. There's no traditional turntable inside. Instead, you can simply place down your dishes. This makes the interior footprint larger meaning it can accommodate larger dishes and even multiple items at once. </p><p>And, thankfully, you don't have to worry about those multiple items not getting an even cook. Panasonic's smart technology has got you covered.</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/us/best-microwaves,review-6316.html">Best microwaves in 2026</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/5-game-changing-microwave-hacks-you-didnt-know-you-could-do?utm_source=facebook&utm_medium=social&utm_campaign=dhfacebook&utm_content=null&fbclid=IwY2xjawSfQ6pleHRuA2FlbQIxMQBicmlkETFBVnFha2lEemhMS1o3VWpHc3J0YwZhcHBfaWQQMjIyMDM5MTc4ODIwMDg5MgABHsgDmFbQDVUAXXFttZ10HdZIFQwDrwXcow8Rej5qYs6BORz3kIBoR4Cs33AF_aem_wTlZXe7XCRCXiaWxyzN-Rg">5 game-changing microwave hacks you didn’t know you could do</a></li><li><a href="https://www.tomsguide.com/home/this-zero-effort-vinegar-hack-is-a-total-game-changer-for-cleaning-your-microwave">This ‘zero effort’ vinegar hack is great for cleaning your microwave</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tried this genius money-saving hack to give my plants a boost — all you need is a box of matches ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/i-tried-this-genius-money-saving-hack-to-give-my-plants-a-boost-all-you-need-is-a-box-of-matches</link>
                                                                            <description>
                            <![CDATA[ I'm always on the lookout for money-saving garden hacks and this one using just a box of matches surprised me – here's what happened and how to do it yourself. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">32LJdWMmh79y76W2gzYT64</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/jocQFFyf2k9NtRrjoB9TcW-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 16 Jun 2026 08:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></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/jocQFFyf2k9NtRrjoB9TcW-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Using matches to make a fertilizer water for the garden]]></media:description>                                                            <media:text><![CDATA[Using matches to make a fertilizer water for the garden]]></media:text>
                                <media:title type="plain"><![CDATA[Using matches to make a fertilizer water for the garden]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/jocQFFyf2k9NtRrjoB9TcW-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>My plants have been looking a little weary lately. I'm tempted to blame the sunny weather, but I fear that I'm just not giving them the attention they need. So, I headed to social media to see if I could find a hack that would help me out (and not cost me a lot of money).</p><p>After lots of searching, I finally stumbled upon something I'd never heard of before – using matches to create my very own plant-boosting fertilizer. It's a staple of my messy kitchen drawer that I usually only pull out when I need to light a birthday candle. Suddenly, I'm looking at that little box of matches in a totally different light. Here's what happened when I followed the simple hack to help feed my plants.</p><h2 id="how-does-it-work-5">How does it work?</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="high" data-lazy-src="https://www.youtube-nocookie.com/embed/tzcRJA6BUrY" allowfullscreen></iframe></div></div><p>YouTube creator, <a href="https://www.youtube.com/@creative_explained/shorts" target="_blank" rel="nofollow">@creative_explained</a>, has a page full of clever tips and tricks to use around your home that he shares with his almost 5 million subscribers (one of which is me). He taught me a clever <a href="https://www.tomsguide.com/home/gardening/this-cheap-gardening-hack-uses-a-common-household-ingredient-youre-probably-throwing-away">potato peel hack to supercharge my garden</a> that's saved me a bunch of money on fertilizer and he's even taught me <a href="https://www.tomsguide.com/home/this-tiktok-hack-claims-vinegar-will-clean-your-shower-head-i-asked-my-plumber-husband-if-it-actually-works">how to clean a shower head with vinegar</a>. </p><p>Sometimes, I like to head back into the archives on his page to see if there's any hacks I've missed and I was surprised by what I found. The video, featured above, reveals that you can feed your plants and give them a natural boost with just a box of matches and some water. Since I already have both those things in my home, I thought it was definitely worth a try – and it turns out it's <em>really</em> simple.</p><p><strong>All you have to do is this:</strong></p><ul><li><strong>Put 5 to 10 matches into a container</strong></li><li><strong>Fill the container with some water</strong></li><li><strong>Wait for around an hour</strong></li><li><strong>Give them a stir and use the pink water to pour onto your plants</strong></li></ul><p>He explains that by letting the matches soak in the water, it'll turn pink: "That's the phosphorous and sulfur being released into the water," he explains. "Pretty much what that means is that it's food for your plants! Phosphorous will help your plants grow these nice, healthy roots and sulfur will produce nice leafy greens." </p><p>But, while in the video you'll see him light the matches and let them burn, it turns out you don't want to do that – and I learned this first-hand when I tried it out.</p><div class="product"><a data-dimension112="1c9c8951-38b0-4fc4-9f9d-e36e6829f168" data-action="Deal Block" data-label="You may not think you need 10 boxes of matches, but there's plenty of ways to use them round your home from lighting tealight and birthday candles – and now for helping your plants grow. You get 32 matches per box, which means 320 matches in total for just over $5." data-dimension48="You may not think you need 10 boxes of matches, but there's plenty of ways to use them round your home from lighting tealight and birthday candles – and now for helping your plants grow. You get 32 matches per box, which means 320 matches in total for just over $5." data-dimension25="$5.49" href="https://www.amazon.com/GreenLight-Diamond-Strike-Matches-Count/dp/B005XP4FKI" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1122px;"><p class="vanilla-image-block" style="padding-top:114.26%;"><img id="rgJfbxVH6HopodjqVBPD2c" name="matches_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/rgJfbxVH6HopodjqVBPD2c.png" mos="" align="middle" fullscreen="" width="1122" height="1282" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>You may not think you need 10 boxes of matches, but there's plenty of ways to use them round your home from lighting tealight and birthday candles – and now for helping your plants grow. You get 32 matches per box, which means 320 matches in total for just over $5. <a class="view-deal button" href="https://www.amazon.com/GreenLight-Diamond-Strike-Matches-Count/dp/B005XP4FKI" target="_blank" rel="nofollow" data-dimension112="1c9c8951-38b0-4fc4-9f9d-e36e6829f168" data-action="Deal Block" data-label="You may not think you need 10 boxes of matches, but there's plenty of ways to use them round your home from lighting tealight and birthday candles – and now for helping your plants grow. You get 32 matches per box, which means 320 matches in total for just over $5." data-dimension48="You may not think you need 10 boxes of matches, but there's plenty of ways to use them round your home from lighting tealight and birthday candles – and now for helping your plants grow. You get 32 matches per box, which means 320 matches in total for just over $5." data-dimension25="$5.49">View Deal</a></p></div><h2 id="here-s-what-happened-when-i-tried-it-5">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="fPQWq6W68LXSzJRHWVNvQW" name="matches_hack_1" alt="Using matches to make a fertilizer water for the garden" src="https://cdn.mos.cms.futurecdn.net/fPQWq6W68LXSzJRHWVNvQW.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>As mentioned, I made the mistake of lighting the matches when I first gave this a try. I'd seen the flames that @creative_explained had lit on his video and hadn't quite paid full attention that it was just for show. Look a little closer and you'll find that the matches inside the water are still red-tipped and that's because he hasn't let them burn.</p><p>This is an important part of this hack because if you burn them, you're eliminating the phosphorous and sulfur that you <em>need</em> for the water to turn pink and nutritious for your plants. So, after a couple of failed attempts, I used the matches without letting them burn. Lesson learned.</p><p>I also thought I'd like to use a little more water than he demonstrates with to make sure I could feed the numerous plants around my home. So, I added 10 matches in one go to see if that would help infiltrate more water – and it did.</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="WvvTxbz9pYDRwbTfPXwUcW" name="matches_hack_3" alt="Using matches to make a fertilizer water for the garden" src="https://cdn.mos.cms.futurecdn.net/WvvTxbz9pYDRwbTfPXwUcW.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>Now, I've only just started trying this new match-infused water trick on my plants, so it's hard to say whether it's making any difference <em>yet. </em>What I do know is that it was super simple to do, very affordable and I haven't seen any damaging effects to the plants. </p><p>So, I'm wiling to keep trying it out using <a href="https://www.tomsguide.com/home/my-moms-thirsty-thursdays-routine-keeps-plants-thriving-and-heres-why-i-swear-by-it">my mom's 'Thirsty Thursday' routine</a> in collaboration with the matches. Since it's so low-cost and quick, it's easy to play the long game and see if my plants get an extra boost. Either way, I feel like I'm creating potions and that's made remembering to water my plants a lot more fun, too.</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/gardening/this-cheap-garden-hack-will-help-your-roses-grow-and-all-you-need-is-a-few-old-tea-bags">This cheap garden hack will help your roses grow — and all you need is a few old tea bags</a></li><li><a href="https://www.tomsguide.com/home/ive-just-discovered-this-strange-hack-thatll-keep-pests-away-from-your-plants-and-it-uses-a-popular-cold-medicine">I've just discovered this strange hack that'll keep pests away from your plants — and it uses a popular cold medicine</a></li><li><a href="https://www.tomsguide.com/home/gardening/this-garden-hack-will-help-your-plants-thrive-and-all-you-need-to-do-is-boil-some-eggs">This garden hack will help your plants thrive — and all you need to do is boil some eggs</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The Philips Skylight arrived with an expensive price tag — here are some cheaper alternatives that won't break the bank ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/smart-home/the-philips-skylight-arrived-with-an-expensive-price-tag-here-are-some-cheaper-alternatives-that-wont-break-the-bank</link>
                                                                            <description>
                            <![CDATA[ The Philips Skylight emulates daylight in areas of your home where natural light is lacking. But with the high asking price, here are some alternatives worth considering. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">fA5qCU998UkAi6gWcdaLKU</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/2LJesNtTVr2n2n392ReT8M-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 15 Jun 2026 10:43:55 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Smart Home]]></category>
                                                    <category><![CDATA[Home]]></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/2LJesNtTVr2n2n392ReT8M-1280-80.jpg">
                                                            <media:credit><![CDATA[Signify]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Philips Skylight on a ceiling]]></media:description>                                                            <media:text><![CDATA[The Philips Skylight on a ceiling]]></media:text>
                                <media:title type="plain"><![CDATA[The Philips Skylight on a ceiling]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/2LJesNtTVr2n2n392ReT8M-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Philips is a brand that knows a lot about having fun with <a href="https://www.tomsguide.com/us/best-smart-lights,review-6194.html">smart lights</a>. Philips Hue lights, for example, can be positioned around your home illuminating rooms in a variety of ingenious ways.</p><p>And now, Signify, the manufacturer of Philips' products, has released something <em>new</em>. The Philips Skylight is designed to bring the feeling of daylight right into your home.</p><p>The range aims to recreate the natural brightness, depth and rhythm of sunlight within your home. Why? Well, <a href="https://www.signify.com/en-us/our-company/news/press-releases/2026/20260806-philips-skylight-designed-to-bring-the-feeling-of-daylight-indoors" target="_blank" rel="nofollow">Signify</a> did some research across 2,000 adults and concluded: "58% feel they spend too much time indoors, and 89% say they feel more energetic when they get enough daylight."</p><h2 id="bring-the-outdoors-in">Bring the outdoors in</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="V9ZbHeNd5hYKNxEC3GNC9M" name="philips_skylight_2" alt="The Philips Skylight on a ceiling" src="https://cdn.mos.cms.futurecdn.net/V9ZbHeNd5hYKNxEC3GNC9M.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Choose aesthetic lighting with simple voice or text commands </span><span class="credit" itemprop="copyrightHolder">(Image credit: Signify)</span></figcaption></figure><p>Using Signify's NatureConnect technology and BioUp technology, Skylight automatically adjusts brightness and tone throughout the day, creating a brighter blue-enriched light in the day and a warmer light at night. </p><p>Users can also take advantage of five preset scenes that are inspired by the daylight that you can adjust to fit your mood or the ambience you're after. Plus, with a surface-mounted slim ceiling profile, it'll fit in nicely with your home.</p><p>This all sounds really wonderful, but you'll have to wait until September 2026 for it to become available for purchase in the U.S. Another sticking point might be how much it costs, which for some may be a little prohibitive. </p><p>Starting at 499.99 euros — around $580 — it's a big investment. But there are some cheaper alternatives that could be worth considering, especially if you don't want to wait.</p><h2 id="philips-skylight-alternatives-available-now">Philips Skylight alternatives (available now)</h2><div class="product"><a data-dimension112="740be59c-6f4c-47ef-a529-60f6894d4291" data-action="Deal Block" data-label="The Nanoleaf Skylight Smarter Kit are modular flush-mounted ceiling lights that mimic the sky. With each panel, you can customize where they're placed in your home. The kits start at $149.99 (on sale) for a 3-Pack, but is also available in 4, 6, 9 and 12, as well being able to buy singular tiles in the expansion pack for just $49.99. The Nanoleaf are currently on clearance though, so may not be around forever." data-dimension48="The Nanoleaf Skylight Smarter Kit are modular flush-mounted ceiling lights that mimic the sky. With each panel, you can customize where they're placed in your home. The kits start at $149.99 (on sale) for a 3-Pack, but is also available in 4, 6, 9 and 12, as well being able to buy singular tiles in the expansion pack for just $49.99. The Nanoleaf are currently on clearance though, so may not be around forever." data-dimension25="$149" href="https://us-shop.nanoleaf.me/products/nanoleaf-skylight-smarter-kit" 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="QHsuEmfw4po8tNzUkBEHtV" name="nanoleaf_skylight_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/QHsuEmfw4po8tNzUkBEHtV.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Nanoleaf Skylight Smarter Kit are modular flush-mounted ceiling lights that mimic the sky. With each panel, you can customize where they're placed in your home. The kits start at $149.99 (on sale) for a 3-Pack, but is also available in 4, 6, 9 and 12, as well being able to buy singular tiles in the expansion pack for just $49.99. The Nanoleaf are currently on clearance though, so may not be around forever.<a class="view-deal button" href="https://us-shop.nanoleaf.me/products/nanoleaf-skylight-smarter-kit" target="_blank" rel="nofollow" data-dimension112="740be59c-6f4c-47ef-a529-60f6894d4291" data-action="Deal Block" data-label="The Nanoleaf Skylight Smarter Kit are modular flush-mounted ceiling lights that mimic the sky. With each panel, you can customize where they're placed in your home. The kits start at $149.99 (on sale) for a 3-Pack, but is also available in 4, 6, 9 and 12, as well being able to buy singular tiles in the expansion pack for just $49.99. The Nanoleaf are currently on clearance though, so may not be around forever." data-dimension48="The Nanoleaf Skylight Smarter Kit are modular flush-mounted ceiling lights that mimic the sky. With each panel, you can customize where they're placed in your home. The kits start at $149.99 (on sale) for a 3-Pack, but is also available in 4, 6, 9 and 12, as well being able to buy singular tiles in the expansion pack for just $49.99. The Nanoleaf are currently on clearance though, so may not be around forever." data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="35e6fa1a-f6b4-43f1-b1e8-fa00b6201942" data-action="Deal Block" data-label="Unveiled at CES 2026, the Govee Smart Ceiling Light Ultra can emulate the sky with ultra-dense pixel precision. It's a little more creative than the other skylights on offer with 100 present scenes and 144 rhythmic combinations to explore. Plus, it's Matter-certified meaning you can link it up to your current smart home ecosystem." data-dimension48="Unveiled at CES 2026, the Govee Smart Ceiling Light Ultra can emulate the sky with ultra-dense pixel precision. It's a little more creative than the other skylights on offer with 100 present scenes and 144 rhythmic combinations to explore. Plus, it's Matter-certified meaning you can link it up to your current smart home ecosystem." data-dimension25="$249" href="https://us.govee.com/products/govee-21-inch-smart-ceiling-light-ultra" 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="yzb6w6mDYRkgyKte4dFhKV" name="govee_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yzb6w6mDYRkgyKte4dFhKV.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Unveiled at CES 2026, the Govee Smart Ceiling Light Ultra can emulate the sky with ultra-dense pixel precision. It's a little more creative than the other skylights on offer with 100 present scenes and 144 rhythmic combinations to explore. Plus, it's Matter-certified meaning you can link it up to your current smart home ecosystem.<a class="view-deal button" href="https://us.govee.com/products/govee-21-inch-smart-ceiling-light-ultra" target="_blank" rel="nofollow" data-dimension112="35e6fa1a-f6b4-43f1-b1e8-fa00b6201942" data-action="Deal Block" data-label="Unveiled at CES 2026, the Govee Smart Ceiling Light Ultra can emulate the sky with ultra-dense pixel precision. It's a little more creative than the other skylights on offer with 100 present scenes and 144 rhythmic combinations to explore. Plus, it's Matter-certified meaning you can link it up to your current smart home ecosystem." data-dimension48="Unveiled at CES 2026, the Govee Smart Ceiling Light Ultra can emulate the sky with ultra-dense pixel precision. It's a little more creative than the other skylights on offer with 100 present scenes and 144 rhythmic combinations to explore. Plus, it's Matter-certified meaning you can link it up to your current smart home ecosystem." data-dimension25="$249">View Deal</a></p></div><div class="product"><a data-dimension112="ef38720f-4142-4ef1-a662-b3724e4acf74" data-action="Deal Block" data-label="The Lumary Smart Skylora Sky Light uses AI-based rhythm control to adapt lighting throughout the day, supporting your body's circadian rhythm. You can operate it via a mobile app, voice assistants, or remote control. It's a little different than the Philips Skylight, created in a circular shape instead of replicating a typical sky light window." data-dimension48="The Lumary Smart Skylora Sky Light uses AI-based rhythm control to adapt lighting throughout the day, supporting your body's circadian rhythm. You can operate it via a mobile app, voice assistants, or remote control. It's a little different than the Philips Skylight, created in a circular shape instead of replicating a typical sky light window." data-dimension25="$249" href="https://www.lumarysmart.com/products/lumary-smart-skylora-sky-light" 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="Hr8RBimsn4QS9utxTW9ZZ6" name="lumary_skylight_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Hr8RBimsn4QS9utxTW9ZZ6.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Lumary Smart Skylora Sky Light uses AI-based rhythm control to adapt lighting throughout the day, supporting your body's circadian rhythm. You can operate it via a mobile app, voice assistants, or remote control. It's a little different than the Philips Skylight, created in a circular shape instead of replicating a typical sky light window.<a class="view-deal button" href="https://www.lumarysmart.com/products/lumary-smart-skylora-sky-light" target="_blank" rel="nofollow" data-dimension112="ef38720f-4142-4ef1-a662-b3724e4acf74" data-action="Deal Block" data-label="The Lumary Smart Skylora Sky Light uses AI-based rhythm control to adapt lighting throughout the day, supporting your body's circadian rhythm. You can operate it via a mobile app, voice assistants, or remote control. It's a little different than the Philips Skylight, created in a circular shape instead of replicating a typical sky light window." data-dimension48="The Lumary Smart Skylora Sky Light uses AI-based rhythm control to adapt lighting throughout the day, supporting your body's circadian rhythm. You can operate it via a mobile app, voice assistants, or remote control. It's a little different than the Philips Skylight, created in a circular shape instead of replicating a typical sky light window." data-dimension25="$249">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/smart-home/i-just-gave-my-home-the-ultimate-smart-light-upgrade-and-i-can-control-it-all-with-my-phone">I just gave my home the ultimate smart light upgrade — and I can control it all with my phone</a></li><li><a href="https://www.tomsguide.com/us/philips-hue-bulb-types,review-4161.html">Philips Hue lights: A guide to what each bulb does (and costs)</a></li><li><a href="https://www.tomsguide.com/us/best-smart-lights,review-6194.html">The best smart lights: tried and tested</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I spent 6 months testing this 50-pint dehumidifier — it wasn't quite what I expected ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/home-appliances/pro-breeze-50-pint-dehumidifier-review</link>
                                                                            <description>
                            <![CDATA[ The Pro Breeze Dehumidifier is a decent option for ensuring consistent humidity, although it lacks the pace and range of similarly-priced rivals. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">XbFotqiqyTmZApRXYnVcY5</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/apWug3K2gLmbmBBp3PwuLY-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 12 Jun 2026 15:44:44 +0000</pubDate>                                                                                                                                <updated>Tue, 16 Jun 2026 16:05:29 +0000</updated>
                                                                                                                                            <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Home]]></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/apWug3K2gLmbmBBp3PwuLY-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment]]></media:description>                                                            <media:text><![CDATA[The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment]]></media:text>
                                <media:title type="plain"><![CDATA[The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/apWug3K2gLmbmBBp3PwuLY-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you have a damp problem, getting one of the <a href="https://www.tomsguide.com/home/home-appliances/best-dehumidifiers"><u>best dehumidifiers</u></a> is a no-brainer. These helpful devices draw moisture from your air to prevent damp and mold. Not only is that good for your walls, ceilings and paint, it’s also highly beneficial for your health.</p><p>Unfortunately, my home is rather damp, especially in winter. So I’m always running two dehumidifiers (one upstairs and one downstairs) to keep things in check. Throughout winter and spring 2026, one of those was the Pro Breeze Dehumidifier, which I had for a long-term test.</p><p>The Pro Breeze is a moderately quick dehumidifier, extracting up to 50 pints / 20 liters a day, with (according to the manufacturer) a wide coverage of up to 4,000 square feet. It’s designed for larger spaces and/or an entire floor of a house. It’s fairly compact given its capacity and has some helpful features like a status LED and a handle.</p><p>It isn’t perfect, with slow operating during stress tests, slightly confusing controls and a water tank that can be awkward to refit. But could it still be worth buying? Find out in my full ProBreeze Dehumidifier review.</p><h2 class="article-body__section" id="section-pro-breeze-dehumidifier-review-specs"><span>Pro Breeze Dehumidifier 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/Pro-Breeze-Dehumidifier-Humidity-Continuous/dp/B077GZ3TVS?" target="_blank" rel="nofollow">$239</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>31 lbs</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>0.39 x 0.39 x 0.39 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Tank capacity</strong></p></td><td  ><p>9-pint</p></td></tr><tr><td class="firstcol " ><p><strong>Daily capacity</strong></p></td><td  ><p>50-pint</p></td></tr><tr><td class="firstcol " ><p><strong>Hose included</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Coverage</strong></p></td><td  ><p>Up to 4,000 sq ft</p></td></tr><tr><td class="firstcol " ><p><strong>Speeds</strong></p></td><td  ><p>3</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-pro-breeze-dehumidifier-review-price-availability"><span>Pro Breeze Dehumidifier 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="Bgt2eMyYBcNQe6Nraga8RY" name="Pro Breeze 50-Pint Dehumidifier-5" alt="The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment" src="https://cdn.mos.cms.futurecdn.net/Bgt2eMyYBcNQe6Nraga8RY.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>The Pro Breeze Dehumidifier is available in the U.S. from Amazon for <a href="https://www.amazon.com/Pro-Breeze-Dehumidifier-Humidity-Continuous/dp/B077GZ3TVS" target="_blank" rel="nofollow"><u>$239</u></a>, which is essentially the going rate for a decent 50-pint dehumidifier. The <a href="https://www.tomsguide.com/home/home-appliances/goveelife-smart-50-pint-dehumidifier-review"><u>GoveeLife Smart 50-pint Dehumidifier</u></a> also costs $239, while the slower <a href="https://www.tomsguide.com/home/home-appliances/waykar-34-pint-dehumidifier-review"><u>Waykar 34-pint Dehumidifier</u></a> costs $228. The excellent <a href="https://www.tomsguide.com/home/frigidaire-50-pint-dehumidifier-revie"><u>Frigidaire 50-pint Dehumidifier</u></a> costs slightly more at $279, but is an excellent performer — at full price, that’s where my money would go. However, we’ve seen the Pro Breeze drop as low as $138 at Amazon, and at those prices, it’s a solid choice.</p><p>In the U.K., where Pro Breeze is based, this dehumidifier is no longer available. However, Pro Breeze offers a much wider array of dehumidifiers in the British market, so there are plenty more to choose from.</p><h2 class="article-body__section" id="section-pro-breeze-dehumidifier-review-design"><span>Pro Breeze Dehumidifier review: Design</span></h2><p>The Pro Breeze Dehumidifier isn’t going to win any awards for its design. It’s a white, upright slab — hardly prepossessing. In fairness, though, they all look like that. I haven’t seen a good-looking dehumidifier that uses wood or makes any effort to look stylish and homely. They all look like medical equipment that’s been stolen from a hospital.</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="YQusyDdZSsXvHRPL7yFgRY" name="Pro Breeze 50-Pint Dehumidifier-3" alt="The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment" src="https://cdn.mos.cms.futurecdn.net/YQusyDdZSsXvHRPL7yFgRY.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>The top panel of the dehumidifier is particularly ugly and dated-looking in my opinion. My <a href="https://www.amazon.co.uk/dp/B0DLNPW9LQ/ref=twister_B0DWFTYWTN"><u>Bosch Dry 2000</u></a> (not available in the U.S.) looks much more modern with its round display and minimalist touch controls. The Pro Breeze features LED status (red, green and blue) to quickly display humidity levels — these are handy, but again, kinda ugly and dated.</p><p>It’s a fairly tall dehumidifier, but the trade off is that it’s a fairly narrow unit, giving extra versatility with placement. That said, when in use, you should always maintain a gap between any dehumidifier and walls or furniture, so they’re always placed slightly awkwardly anyway.</p><p>I like that the Pro Breeze features a handle and wheels. This makes it super easy to move around to different parts of your home if you need to target a specific area. During winter, I noticed some mold growing in a corner of my hallway, so I quickly scooted the dehumidifier across my wood floor and positioned it very near the problem spot. The wheels aren’t quite as effective on carpet, but hey, they’re still nice to have.</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="eJsmhrYJaJA6ciLK2i7LhY" name="Pro Breeze 50-Pint Dehumidifier-10" alt="The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment" src="https://cdn.mos.cms.futurecdn.net/eJsmhrYJaJA6ciLK2i7LhY.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>The filter is removable and washable, which is great to see, although I wouldn’t advise buying any dehumidifier without this feature. They get such heavy use that replacing filters would cost you a fortune. The GoveeLife Smart Dehumidifier and Waykar Dehumidifier also have washable filters.</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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qQaGPVKk3xwGSFuZVdL5PY" name="Pro Breeze 50-Pint Dehumidifier-2" alt="The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment" src="https://cdn.mos.cms.futurecdn.net/qQaGPVKk3xwGSFuZVdL5PY.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>The Pro Breeze’s controls are clearly labelled and fairly self explanatory. There are buttons for everything you’ll need. However, I found the display a little confusing. There are various modes, many with similar icons, doing different things. There’s a fan speed setting, but also a high power setting — isn’t the high fan speed also the high power setting? There’s a laundry mode that does basically the same things as the high power mode. </p><p>After a read-through of the manual, I realized what these different modes were doing, and that there were subtle differences between them. But it was still a little frustrating, as other dehumidifiers like my Bosch Dry 2000 and the GoveeLife Smart are extremely intuitive and easy-to-use straight out of the box.</p><h2 class="article-body__section" id="section-pro-breeze-dehumidifier-review-performance"><span>Pro Breeze Dehumidifier review: Performance</span></h2><p>The Pro Breeze Dehumidifier will extract up to 50 pints of water from your air per day, according to the manufacturer, which is a lot of moisture. This will depend on specific circumstances, of course, which I wasn’t able to match in my testing. I needed to empty the tank at least once a day with it running constantly — so 9-18 pints of water per day. </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="Bgt2eMyYBcNQe6Nraga8RY" name="Pro Breeze 50-Pint Dehumidifier-5" alt="The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment" src="https://cdn.mos.cms.futurecdn.net/Bgt2eMyYBcNQe6Nraga8RY.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>Pro Breeze’s claims about the dehumidifier’s range are all over the place. The Amazon page for the 50-pint model claims it can cover spaces up to 4,000 square feet, which is enormous. However, the Pro Breeze site only claims 2,000 square feet, while the U.K. Pro Breeze site claims 322 square feet for the equivalent model. The U.K. model has slightly lower energy consumption, but the exact same 195 m³/h airflow rate as the U.S. model… So where is the circa 3,700 square foot difference coming from? It’s a mess.</p><p>Taking the top estimate of 4,000 square feet — that’d be equivalent to a whole storey in a very large house, or the floor of an office building. While this dehumidifier is good, I’m not sure those claims stack up to reality. In fact, as I’ll cover immediately below, I’m more inclined to believe the British rating of 322 square feet…</p><h3 id="testing">Testing</h3><p>My kitchen — the most humid room in my house — measures around 270 square feet. To test the Pro Breeze, I cooked a pot of pasta and two slow-cook sauces on our induction cooker, without lids, and kept our extractor off to generate steam. On the maximum setting, it took the Pro Breeze several hours to take the humidity from red on the LED status to green (from nearly 80% back down closer to 60%). By comparison, our favorite dehumidifier for large spaces, the <a href="https://www.tomsguide.com/home/frigidaire-50-pint-dehumidifier-review"><u>Frigidaire Dehumidifier</u></a>, cleared a basement from 90% to 35% in just 40 minutes, although that was in a smaller 100 square-foot space. </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="PLC4DfZRvd4QzUbzQkt4NY" name="Pro Breeze 50-Pint Dehumidifier-4" alt="The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment" src="https://cdn.mos.cms.futurecdn.net/PLC4DfZRvd4QzUbzQkt4NY.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>In less extreme scenarios, the Pro Breeze performed admirably, though. I left it running in the default de-humidification mode, where you can set the target humidity and fan speed, and it was able to keep the moisture levels in my kitchen stable (until I cooked). </p><p>In my kitchen and in my hallway (where I had mold and condensation), the Pro Breeze did a great job. There was no window condensation in the mornings and once we cleared the mold with some spray, it didn’t return. Importantly, though, the Pro Breeze wasn’t able to dehumidify my entire downstairs — to fight the mold, I had to move the unit from the kitchen to the hallway — so again, those 4,000-square-foot claims seem pretty bogus to me. </p><p>There’s a laundry mode, which is awesome for drying clothes inside during winter. I use this regularly, and the Pro Breeze dries most clothes in a couple of hours if positioned near your dryer. Annoyingly, there’s no timer by default on laundry mode. My Bosch Dry 2000 automatically sets an 8-hour timer in laundry mode for effortless overnight drying. You have to do this manually on the Pro Breeze, otherwise laundry mode will just keep running indefinitely at max, wasting energy.</p><p>The Pro Breeze Dehumidifier gets noticeably warm in use, especially in the higher power modes. It packs out quite a bit of warm air, so you won’t want to sit next to it if you’re hot. I like that the Pro Breeze can be used as a standalone fan, though — when in fan mode, it doesn’t dehumidify but pumps out cooler air, helping to cool rooms down in warmer weather.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/4WjtqFFeuhL7ThxGfc4gjY.jpg" alt="The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment" /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fsCgDmQ9273hotzKqe46YY.jpg" alt="The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment" /><figcaption><small role="credit">Future</small></figcaption></figure></figure><p>The water tank is fairly spacious at 9 pints / 5.1 liters, easy to empty and features a fill window to tell you when it’s full. It’s awkward to get back into place, though, often getting jammed — not a huge deal but a little annoying all the same. The Pro Breeze has an outlet for continuous draining and comes with a rubber pipe, so if you need to run it indefinitely with it draining into a bath or sink, you can — this is ideal for super damp homes needing emergency dehumidification.</p><h3 id="noise-night-mode">Noise & night mode</h3><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/rXVwZvrTLhZM5gs8BnrHh3.jpg" alt="A screenshot showing Decibel X readings for the Pro Breeze 50-pint Dehumidifier" /><figcaption>Normal operation.<small role="credit">Decibel X / Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/xXziSvY6fqpk9AWFvWGPh3.jpg" alt="A screenshot showing Decibel X readings for the Pro Breeze 50-pint Dehumidifier" /><figcaption>Night mode.<small role="credit">Decibel X / Future</small></figcaption></figure></figure><p>The Pro Breeze is rated for 48dB of noise, although I wasn’t able to achieve anything lower than 54dB in normal mode and 52.5 in night mode. The Waykar dehumidifier ran at a lower 49dB in our testing, so may be the one to go for if this is a priority.</p><p>Still, the Pro Breeze isn’t too loud. 54dB is around the same level as other household appliances. It’ll be enough to disturb people who sleep lightly, but the same is true for pretty much every other dehumidifier. You get used to it.</p><p>You can turn off the digital display and LEDs, so the Pro Breeze won’t disturb you light-wise.</p><h2 class="article-body__section" id="section-pro-breeze-dehumidifier-review-app"><span>Pro Breeze Dehumidifier review: App</span></h2><p>In the U.S., there doesn’t appear to be a companion app for the Pro Breeze. In the U.K., there is an app version of the dehumidifier, which is slightly more expensive (and which I’d recommend, as the controls are awkward). I tested the U.K. non-app version.</p><h2 class="article-body__section" id="section-pro-breeze-dehumidifier-review-how-does-it-compare"><span>Pro Breeze Dehumidifier 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="j82KVBEhuKptuboAnsZZjY" name="Pro Breeze 50-Pint Dehumidifier-11" alt="The Pro Breeze 50-pint Dehumidifier on a hard wood floor in a home environment" src="https://cdn.mos.cms.futurecdn.net/j82KVBEhuKptuboAnsZZjY.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>The Pro Breeze Dehumidifier isn’t the best at anything, really. The Frigidaire 50-pint Dehumidifier and GoveeLife Smart 50-pint Dehumidifier are faster, while being similarly priced. Personally, I’d spend a little more and bag the Frigidaire. </p><p>That said, while its high-speed, extreme humidity performance may not be the best, the Pro Breeze still does an excellent job at keeping levels stable longer term. With that in mind, all three rivals are equally ugly, similarly priced, and take up around the same amount of space. So, realistically, you can take your pick.</p><p>In the U.K., I’d definitely recommend my other dehumidifier, the Bosch Dry 2000 (or its bigger sibling, the Dry 4000), instead. Performance is similar, but I find the controls more intuitive and the styling ever-so-slightly nicer.</p><h2 class="article-body__section" id="section-pro-breeze-dehumidifier-review-verdict"><span>Pro Breeze Dehumidifier review: Verdict</span></h2><p>The Pro Breeze Dehumidifier isn’t a bad product. It does a good job at everyday dehumidification, keeping levels low if you run it all the time. It wasn’t the fastest performer we’ve seen, but did the job eventually in high-humidity scenarios. The variety of modes is useful, if not a little confusing at first, and the water tank is spacious and easy to empty, if not a little tricky to refit.</p><p>At the end of the day, if you’re struggling with damp and mold, there are better dehumidifiers out there, but the Pro Breeze isn’t a terrible solution by any stretch. Still, I’d advise waiting for a sale if you can.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Dyson’s 2-in-1 purifier fan is the perfect way to beat the summer heat — except for one major flaw ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/home-appliances/dyson-find-follow-purifier-cool-pc3-review</link>
                                                                            <description>
                            <![CDATA[ The Dyson Find+Follow Purifier Cool PC3 is a powerful air purifier and fan, but comes at a hefty cost. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">VDfUQWh2E5wJwej25pb5YX</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/a6qikmzsY9qQPTGffP9p2S-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 12 Jun 2026 11:43:56 +0000</pubDate>                                                                                                                                <updated>Tue, 16 Jun 2026 16:05:28 +0000</updated>
                                                                                                                                            <category><![CDATA[Home Appliances]]></category>
                                                    <category><![CDATA[Home]]></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/a6qikmzsY9qQPTGffP9p2S-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Dyson Find+Follow Purifier Cool PC3 in a lounge with a blue wall in the background]]></media:description>                                                            <media:text><![CDATA[The Dyson Find+Follow Purifier Cool PC3 in a lounge with a blue wall in the background]]></media:text>
                                <media:title type="plain"><![CDATA[The Dyson Find+Follow Purifier Cool PC3 in a lounge with a blue wall in the background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/a6qikmzsY9qQPTGffP9p2S-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Dyson Find+Follow Purifier Cool PC3 (bit of a tongue-twister, right?) is a 2-in-1 floor fan and air purifier, designed to help keep your home air healthy while you stay cool.</p><p>As you can probably already tell from the image up there, it looks about as quintessentially Dyson as it’s possible to get and comes with equally Dyson-esque build quality. It’s a strong air purifier and boasts a range of useful features in its app. Plus, none of its rivals on our roundup of the <a href="https://www.tomsguide.com/best-picks/best-air-purifier"><u>best air purifiers</u></a> include a fan, either.</p><p>Unfortunately, the Dyson looks and build quality also bring the inevitable Dyson premium: this thing is extremely expensive. The cooling function, meanwhile, isn’t much better than that of a standard blade fan.</p><p>But could this still be the right air purifier for you? Find out in my full Dyson Find+Follow Purifier Cool PC3 review.</p><h2 class="article-body__section" id="section-dyson-find-follow-purifier-cool-pc3-review-specs"><span>Dyson Find+Follow Purifier Cool PC3 review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.dyson.com/air-treatment/air-purifiers/find-follow-purifier-cool/white-gold" target="_blank" rel="nofollow"><u>$849</u></a> / <a href="https://www.dyson.co.uk/air-treatment/purifiers/find-follow-purifier-cool/white-gold" target="_blank" rel="nofollow"><u>£549</u></a></p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>41.3 x 8.6 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>12 lbs</p></td></tr><tr><td class="firstcol " ><p><strong>Room coverage</strong></p></td><td  ><p>290 sq ft</p></td></tr><tr><td class="firstcol " ><p><strong>Air flow</strong></p></td><td  ><p>76 gallons / sec</p></td></tr><tr><td class="firstcol " ><p><strong>Filter(s) type</strong></p></td><td  ><p>HEPA H13; K Carbon; SCO</p></td></tr><tr><td class="firstcol " ><p><strong>Oscillation angle</strong></p></td><td  ><p>350 degrees</p></td></tr><tr><td class="firstcol " ><p><strong>App connectivity</strong></p></td><td  ><p>Yes</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-dyson-find-follow-purifier-cool-pc3-review-price-availability"><span>Dyson Find+Follow Purifier Cool PC3 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="45DCzmx7usoYQSPwte4zaR" name="EmptyName 3.JPG" alt="The Dyson Find+Follow Purifier Cool PC3" src="https://cdn.mos.cms.futurecdn.net/45DCzmx7usoYQSPwte4zaR.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>The Dyson Find+Follow PC3 costs <a href="https://www.dyson.com/air-treatment/air-purifiers/find-follow-purifier-cool/white-gold" target="_blank" rel="nofollow"><u>$849 from Dyson in the U.S.</u></a> and <a href="https://www.dyson.co.uk/air-treatment/purifiers/find-follow-purifier-cool/white-gold" target="_blank" rel="nofollow"><u>£549 in the U.K.</u></a>, so you’re paying the usual Dyson premium. The U.S. price is especially high, and if you can’t stretch that far, I’d advise looking at the <a href="https://www.tomsguide.com/home/ive-been-using-the-windmill-air-purifier-for-a-month-and-it-performs-as-well-as-it-looks"><u>Windmill Air Purifier</u></a> ($299) or <a href="https://www.tomsguide.com/home/home-appliances/blueair-blue-signature-air-purifier-review"><u>Blueair Blue Signature</u></a> ($499, although often discounted at Amazon) — these are two of our favorite air purifiers.</p><p>The Dyson somewhat justifies an enlarged price tag over rivals. Firstly, it’s also a fan, which the Windmill and Blueair aren’t. It packs the usual beautiful Dyson build quality and beautiful modern styling; plus connectivity with the Dyson app. So, if you already use Dyson gear, the PC3 will blend in seamlessly from both an aesthetic and user-interface standpoint.</p><p>Still, $849 is wildly expensive, and there isn’t really any way to make it make sense. It’s an extravagance. You want Dyson, you have to pay the Dyson tax.</p><h2 class="article-body__section" id="section-dyson-find-follow-purifier-cool-pc3-review-design"><span>Dyson Find+Follow Purifier Cool PC3 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="fYNbMsLauZLMrhjwAqqULR" name="EmptyName 2.JPG" alt="The Dyson Find+Follow Purifier Cool PC3 filter compartment" src="https://cdn.mos.cms.futurecdn.net/fYNbMsLauZLMrhjwAqqULR.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>The PC3 design is archetypal Dyson — gloss-white finish; premium bronze base; confident curves contrasted with clean, straight lines. It’s pure neo-noughties, and I love it. It pairs extremely well with my DysonSolarcycle Morph desk lamp, which I also have in a bronze finish.</p><p>It feels like a Dyson product, too — extremely high quality. I moved my two review units around the house a lot during testing. Annoyingly, there are no wheels, so I simply scooped my forearm through the top of the fan loop to carry it around. I neither heard nor felt a single groan or unnerving sound while doing so.</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="gsr7P2rdb4xwScj95CY66S" name="EmptyName 8.JPG" alt="The Dyson Find+Follow Purifier Cool PC3 with its filters removed" src="https://cdn.mos.cms.futurecdn.net/gsr7P2rdb4xwScj95CY66S.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>The air purifier uses HEPA filters, which are the bare minimum you should look for in any air purifier or vacuum cleaner. These filter out 99.95% of particles, including viruses, bacteria, spores, pollen and pet dander. There’s a carbon filter to trap the potentially harmful byproducts of combustion, including NO2; and a Selective Catalytic Oxidation (SCO) filter, which traps toxic formaldehyde and, according to Dyson, never needs replacing.</p><h2 class="article-body__section" id="section-dyson-find-follow-purifier-cool-pc3-review-setup-use"><span>Dyson Find+Follow Purifier Cool PC3 review: Setup & use</span></h2><p>The Dyson PC3 is extremely easy to set up as it comes pre-assembled. You simply unbox and unpackage it and it’s basically ready to go. You can use it without the Dyson app, but I wouldn’t advise doing so. There’s a QR code on the packaging for downloading the app.</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="KKdPLMBNqKEEjou48iaX5S" name="EmptyName 9.JPG" alt="The Dyson Find+Follow Purifier Cool PC3 remote in hand" src="https://cdn.mos.cms.futurecdn.net/KKdPLMBNqKEEjou48iaX5S.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>Once in the app, there’s a simple setup process to pair the fan to your app via Bluetooth. This works flawlessly each time (I had two PC3s to set up) and takes a minute or so. You then choose your room and, <em>voilà</em>, you can use the app as a remote control. The app is fantastic, giving you control over modes, fan speed, precise fan direction and rotation, and air purification. The controls are straightforward and all self-explanatory. </p><p>There’s a remote control included, which attaches to the top of the fan magnetically and features the same controls as in the app. But although any changes you make on the remote will be shown on the PC3’s LED display, it’s still way more intuitive to see exactly what you’re doing in the Dyson app. </p><p>The remote’s buttons aren’t backlit, either, so it’s impossible to see what you’re pressing at nighttime. It’s handy for making basic adjustments during the day, I guess, and will be necessary if multiple people want to adjust the fan (visitors, or in an office).</p><h2 class="article-body__section" id="section-dyson-find-follow-purifier-cool-pc3-review-performance"><span>Dyson Find+Follow Purifier Cool PC3 review: Performance</span></h2><p>The PC3 serves dual purposes. Not only is it an air purifier, but one of Dyson’s renowned standing cooling fans. I’ll evaluate both separately, as well as the Find+Follow mode and noise levels.</p><h4 id="air-purifier">Air purifier</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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jdCeynG67GpVMa472K7yxR" name="EmptyName 4.JPG" alt="The Dyson Find+Follow Purifier Cool PC3 LED screen" src="https://cdn.mos.cms.futurecdn.net/jdCeynG67GpVMa472K7yxR.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>Firstly, I’ll discuss the PC3’s air purification, which is pretty good. According to Dyson, a single fan purifies rooms up to 27m², filtering over 290 litres per second. The PC3 filters and tracks a variety of contaminants, which can be viewed in basic form on the display, or in detail via the app. </p><ul><li><strong>PM2.5</strong> — Particulate matter smaller than 2.5 microns, including smoke, burning candles and industrial emissions</li><li><strong>PM10 </strong>— Particular matter smaller than 10 microns, including pollen and allergens</li><li><strong>VOCs</strong> — Volatile organic compounds, such as cooking odors, burning fuel, perfume and cleaning products</li><li><strong>NO2</strong> — Nitrogen dioxide and oxidizing gases, the remnants from combustion, including from cooking and vehicles</li><li>The PC3 also tracks general air quality, which is affected by all the above</li></ul><p>Initially, I ran the air purifier in my living room, which is next to a busy road, keeping the windows open to see if there was any spike in NO2 emissions. If there were, these proved easy work for the PC3, which kept NO2 and air quality in the green.</p><p>I ran the same tests in my kitchen with the doors and windows to my garden open, to induce some PM10 allergens like pollen into the room. The grass pollen count in my area was moderate to high, but even so I couldn’t get a reading on the PC3.</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:5333px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="DqzxT6ihF42ksMhLBptK7R" name="Dyson PC3 — Test 1" alt="The Dyson Find+Follow Purifier Cool PC3 being controlled in the Dyson app" src="https://cdn.mos.cms.futurecdn.net/DqzxT6ihF42ksMhLBptK7R.jpg" mos="" align="middle" fullscreen="1" width="5333" height="3000" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/DqzxT6ihF42ksMhLBptK7R.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Dyson / Future)</span></figcaption></figure><p>So, I subjected the air purifier to some sterner tests. I first cooked homemade pizza in my oven. After it was done, I deliberately left semolina flour and cornflour to burn on my pizza steel inside the oven. Then I opened the oven door. </p><p>The PM2.5 and PM10 immediately spiked, and my kitchen air quality was sent straight into the red (very poor). Within 30 minutes, the PC3 in auto mode had pulled the air quality back into yellow (fair). By 2.5 hours later, the air quality was back in the green (good).</p><p>I then ran a closed-door, closed-window and closed-curtain test in my bedroom, which is smaller. I sprayed lots of aerosolized dry anti-perspirant, lit a candle and burnt several matches down to the wick before blowing them out and letting them smoke. I could literally taste how disgusting the air was in the room. PM2.5 and PM10 figures went through the roof, this time the worst level: purple (severe). The PC3 automatically ramped up to max power and cleared the room back to green (good) in around 30-minutes. The smell of the aerosol was cleared earlier, though, in around 10 minutes. </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:5333px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="r9boUcuc36gJ4LTX3mSKJR" name="Dyson PC3 — test 2" alt="The Dyson Find+Follow Purifier Cool PC3 being controlled in the Dyson app" src="https://cdn.mos.cms.futurecdn.net/r9boUcuc36gJ4LTX3mSKJR.jpg" mos="" align="middle" fullscreen="1" width="5333" height="3000" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/r9boUcuc36gJ4LTX3mSKJR.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Dyson / Future)</span></figcaption></figure><p>These rates are fairly good in my book. The <a href="https://www.tomsguide.com/home/ive-been-using-the-windmill-air-purifier-for-a-month-and-it-performs-as-well-as-it-looks"><u>Windmill Air Purifier</u></a> and <a href="https://www.tomsguide.com/home/home-appliances/blueair-blue-signature-air-purifier-review"><u>Blueair Blue Signature</u></a> — two of our favorite air purifiers — will clear candle smoke or a handful of flour in minutes, but the test I subjected the Dyson to was way more extreme, and it only took half an hour to get air quality levels from severe back to good.</p><h4 id="cooling-fan">Cooling fan</h4><p>I tested the PC3 at the perfect time — a heatwave hit the U.K. just two days after I started testing. Temperatures hit 33C, or 91F, and if you’ve ever been to Britain in the summer, you’ll know we don’t do aircon in our homes.</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="z2K32ndM6gg9ViF7QqZHkR" name="EmptyName 11.JPG" alt="The Dyson Find+Follow Purifier Cool PC3 top of the fan" src="https://cdn.mos.cms.futurecdn.net/z2K32ndM6gg9ViF7QqZHkR.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>The PC3 definitely helped keep my wife and I cooler than if we’d had no fan. Unfortunately, though, the PC3 isn’t worth spending the money on if you’re only after a cooling fan. The performance wasn’t any better than my $50 conventional bladed fan.</p><p>At night, with the PC3 at the foot of my bed and running at full blast, the fan wasn’t strong enough for air to reach my face and keep me cool. I had to dig out my small bladed fan to put on my bedside table. At close quarters, this felt as powerful and cooling as the Dyson.</p><p>On a positive note, the PC3 has a rear diffusion mode, which is useful. This diffuses cooler air out of the rear of the fan loop, rather than directly at you through the front — it’s better for gentler, longer-term cooling in less extreme ambient temperatures.</p><h4 id="find-follow">Find+Follow</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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Y5tNcsi6kPhdRy5mxPsudR" name="EmptyName 5.JPG" alt="The Dyson Find+Follow Purifier Cool PC3 detection camera" src="https://cdn.mos.cms.futurecdn.net/Y5tNcsi6kPhdRy5mxPsudR.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>The PC3’s Find+Follow setting uses a sensor and subject detection to detect, lock onto and track subjects round the room, following them with the fan (there’s no recording taking place, though, for the security conscious). It’s kinda cool, accurately detecting and following you when you walk nearby. With more than one person detected, it’ll automatically rotate to hit them both, so no need to keep setting rotation angles manually as people move.</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:5333px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pwA3xhL3gLNZeFWvNLkh5R" name="Dyson PC3 — F+F" alt="The Dyson Find+Follow Purifier Cool PC3 being controlled in the Dyson app" src="https://cdn.mos.cms.futurecdn.net/pwA3xhL3gLNZeFWvNLkh5R.jpg" mos="" align="middle" fullscreen="1" width="5333" height="3000" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/pwA3xhL3gLNZeFWvNLkh5R.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Dyson / Future)</span></figcaption></figure><p>There are a few problems, though. Firstly, it doesn’t work with animals. In the hot weather, I wanted my dogs to get some breeze, so I had to set the rotation angle manually. Secondly, the detection range is a little narrow, so it failed to find me a few times, and I had to manually start a detection sweep. Finally, it had trouble recognizing people when they sat down on a low sofa or lay down, meaning I had to set the rotation manually again. These issues undermined the hands-off experience Find+Follow is supposed to allow.</p><h4 id="noise-levels">Noise levels</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:5333px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="SpSfFmkF5CAkiJLjYpVy4R" name="Dyson PC3 — DBX" alt="The Dyson Find+Follow Purifier Cool PC3's sound being measured in the Decibel X app, with 56.2dB being the average volume." src="https://cdn.mos.cms.futurecdn.net/SpSfFmkF5CAkiJLjYpVy4R.jpg" mos="" align="middle" fullscreen="1" width="5333" height="3000" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/SpSfFmkF5CAkiJLjYpVy4R.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Decibel X / Future)</span></figcaption></figure><p>The PC3 obviously makes noise. It’s a whopping great big fan. It isn’t <em>that</em> loud though, and at full power (10) it was peaking at 57.6dB on DecibelX — equating to other household appliances like a dishwasher. There’s a night mode, which turns off the display and pares back the noise a little, but at its quietest, the PC3 still operates at 50dB.</p><p>This is obviously rather noisy in the dead of night. I’m used to running a dehumidifier at night, so running the PC3 at full pelt didn’t bother me. But if you’re a sensitive sleeper, the noise may still be an issue. Check out the <a href="https://www.tomsguide.com/home/smart-home/dyson-hushjet-air-purifier"><u>Dyson HushJet Compact Air Purifier</u></a> instead, which operates at just 27dB.</p><h2 class="article-body__section" id="section-dyson-find-follow-purifier-cool-pc3-review-app"><span>Dyson Find+Follow Purifier Cool PC3 review: App</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:5333px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rPXaXqv4rLgbZszzFJJ2xQ" name="Dyson PC3 — App" alt="The Dyson Find+Follow Purifier Cool PC3 being controlled in the Dyson app" src="https://cdn.mos.cms.futurecdn.net/rPXaXqv4rLgbZszzFJJ2xQ.jpg" mos="" align="middle" fullscreen="1" width="5333" height="3000" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/rPXaXqv4rLgbZszzFJJ2xQ.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Dyson / Future)</span></figcaption></figure><p>The Dyson app is fairly good. It’s a little janky sometimes, failing to connect to my Dyson Solarcycle Morph desk lamp. But with the PC3, I have no complaints. As I’ve detailed throughout, the app is by far the easiest and most insightful way to control the PC3. Being able to visualize the controls on your smartphone instead of the tiny LED screen is super handy. And then there are the in-depth graphs to show you real-time and longer-term air quality. Without the app, I’m not sure I could’ve scored this product as highly.</p><h2 class="article-body__section" id="section-dyson-find-follow-purifier-cool-pc3-review-storage-maintenance"><span>Dyson Find+Follow Purifier Cool PC3 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="HyyVqTrNdDDjWmfBewRRZR" name="EmptyName 7.JPG" alt="The Dyson Find+Follow Purifier Cool PC3 filter compartment being removed" src="https://cdn.mos.cms.futurecdn.net/HyyVqTrNdDDjWmfBewRRZR.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>If (like me) you don’t have a large house, storing this badboy is gonna be a problem. The PC3 is very tall, and although narrow (making it easy to fit into narrow spaces), still takes up as much real estate as a floor lamp. If you’ve already got a floor lamp where a floor lamp should go, well, the PC3 will need to stand awkwardly wherever it can. And with no folding or dismantling possible, what you see is what you get.</p><p>Maintenance is fairly simple: just replace the filters when needed. Dyson recommends changing every 12-months, or when the app/LED counter reaches 0%. I ran both my fans at full tilt for around a week (day and night), and the percentage dropped to 97%. That was quite an extreme test, though, and home users should only need to replace one a year.</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:5333px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="MEMPVMZn8kNQ9EZVA784zQ" name="Dyson PC3 — Filters" alt="The Dyson Find+Follow Purifier Cool PC3 being controlled in the Dyson app" src="https://cdn.mos.cms.futurecdn.net/MEMPVMZn8kNQ9EZVA784zQ.jpg" mos="" align="middle" fullscreen="1" width="5333" height="3000" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/MEMPVMZn8kNQ9EZVA784zQ.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Dyson / Future)</span></figcaption></figure><p>The PC3 uses Dyson’s 360-degree combined HEPA H13 + K-Carbon filter, which costs <a href="https://www.dyson.com/support/journey/tools/974527-01" target="_blank" rel="nofollow"><u>$89</u></a> / <a href="https://www.dyson.co.uk/support/journey/tools/974527-01" target="_blank" rel="nofollow"><u>£85</u></a> each. Replacing the filters couldn’t be easier: press the button to release the case covers, remove the filters, insert new ones, then close it all back up again.</p><h2 class="article-body__section" id="section-dyson-find-follow-purifier-cool-pc3-review-how-does-it-compare"><span>Dyson Find+Follow Purifier Cool PC3 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.25%;"><img id="xXTRCuCXaiQsGZ7Edac4VR" name="EmptyName 12.JPG" alt="The Dyson Find+Follow Purifier Cool PC3 remote on the top of the fan" src="https://cdn.mos.cms.futurecdn.net/xXTRCuCXaiQsGZ7Edac4VR.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>In terms of its purifying, the Dyson is a match for other large rivals like the Windmill Air Purifier, albeit much more expensive and bulky. The Windmill is much more understated, design-wise, blending in perfectly alongside a laundry basket in our review — that might be the one to go for if you’re not a fan of the Dyson’s “statement piece” design. If you’re short on space (or indeed cash), meanwhile, the Blueair Blue Signature or Shark NeverChange Compact Pro are much better choices.</p><p>I’d only really recommend spending extra on the PC3 over any of its rivals if you have other Dyson products using the app and/or you need a 2-in-1 fan and purifier — none of our favorite air purifiers offer that. Still, you can grab a cheaper, older model like the <a href="https://www.dyson.com/air-treatment/air-purifiers/purifier-cool-pc1/black-nickel"><u>Dyson PC1</u></a> ($549) and get many of the same core benefits.</p><h2 class="article-body__section" id="section-dyson-find-follow-purifier-cool-pc3-review-verdict"><span>Dyson Find+Follow Purifier Cool PC3 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="a6qikmzsY9qQPTGffP9p2S" name="EmptyName.JPG" alt="The Dyson Find+Follow Purifier Cool PC3 in a lounge with a blue wall in the background" src="https://cdn.mos.cms.futurecdn.net/a6qikmzsY9qQPTGffP9p2S.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>The Dyson Find+Follow Purifier Cool PC3 is an excellent air purifier, cleaning extremely polluted air in under 30 minutes during testing. For moderate day-to-day use, it’ll have no issues keeping the air in your home healthy. It packs a variety of very useful features, not least the Dyson app, which is critical to the PC3’s user-friendliness. There’s also the Find+Follow function, which is cool, but a little flawed. And of course, there’s the built-in floor fan, which our other favorite air purifiers don’t offer.</p><p>That said, it’s heinously expensive in the U.S., and rather difficult to justify. If you really need a 2-in-1 purifier and fan combo, it could be worth the spend. And if you have other recent Dyson gear, the PC3 will fit in both aesthetically and technologically, thanks to the ecosystem-wide Dyson app. </p><p>At the end of the day, the PC3 is a Dyson. It’s expensive, arguably overpriced, but would I give it back? Only if forced.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I made a DIY mini garden for free — all you need is this simple kitchen item ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/i-made-a-diy-mini-garden-for-free-all-you-need-is-this-simple-kitchen-item</link>
                                                                            <description>
                            <![CDATA[ I'm always on the lookout for a new excuse to upcycle, so when I found out you could turn a milk jug into a base for a miniature garden, I had to try it out. Here's what happened. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ArhxACUVbuAL3gdVHgN2X8</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/QsznGtQiN5VKu5Jm4uE33K-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 12 Jun 2026 08:00:00 +0000</pubDate>                                                                                                                                <updated>Fri, 12 Jun 2026 09:09:19 +0000</updated>
                                                                                                                                            <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></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/QsznGtQiN5VKu5Jm4uE33K-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Mini garden in milk carton]]></media:description>                                                            <media:text><![CDATA[Mini garden in milk carton]]></media:text>
                                <media:title type="plain"><![CDATA[Mini garden in milk carton]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/QsznGtQiN5VKu5Jm4uE33K-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Gardening doesn't have to be complicated and it also doesn't have to be expensive. I say this because I'm constantly on the hunt for hacks that'll save me money from <a href="https://www.tomsguide.com/home/gardening/this-cheap-gardening-hack-uses-a-common-household-ingredient-youre-probably-throwing-away">using potato peel to supercharge my garden</a> to <a href="https://www.tomsguide.com/home/gardening/this-cheap-garden-hack-will-help-your-roses-grow-and-all-you-need-is-a-few-old-tea-bags">using old tea bags to help my roses thrive</a>, both of which have seriously surprised me with the results. And during my most recent scrolling frenzy on social media, I stumbled upon a <em>really</em> clever idea. </p><p>I've always wanted to make a miniature garden without having to spend cash on planters and pots. I'm short on space, time and money, so the hack I found was perfect, especially as I have an abundance of succulents looking for a new place to live. What's even better about is that it uses a kitchen item I already had at home (and most likely you do too). For this one, all I needed was an old milk jug. So, instead of putting it in the recycling, here's what I did instead.</p><h2 id="how-does-it-work-6">How does it work?</h2>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@dumpsterbunny/video/7544477808144305439" data-video-id="7544477808144305439" style="max-width: 605px; min-width: 325px;">                        <section>                            <a target="_blank" title="@dumpsterbunny" href="https://www.tiktok.com/@dumpsterbunny">@dumpsterbunny</a>                            <p></p><a target="_blank" title="♬ original sound - Dumpster Bunny" href="https://www.tiktok.com/music/original-sound-7544477850120751903">♬ original sound - Dumpster Bunny</a></section>                    </blockquote></div>                <p>TikTok creator, <a href="https://www.tiktok.com/@dumpsterbunny" target="_blank" rel="nofollow">@dumpsterbunny</a> posts an eclectic mix of videos to her over 35k followers and amongst her hobbies, she shares her love of gardening. As featured above, she's even posted a hack where she takes an old milk jug and totally transforms it into a lovely miniature succulent garden. Thankfully, it's super simple to follow.</p><p><strong>All you have to do is this:</strong></p><ul><li><strong>Cut an empty milk jug in half, just under the handle</strong></li><li><strong>Take the lid off and turn the top half upside down and place it in the bottom half</strong></li><li><strong>If not secure, cut two holes and use a zip-tie to secure the bottom half round the handle</strong></li><li><strong>Add in small rocks to the bottom, add soil on top of that, and then add your miniature plants and/or succulents</strong></li></ul><p>In case you're wondering, the reason behind placing your milk jug upside down with the lid off is to create a drainage system for the bottom of your garden. And by placing the rocks down before the soil, you can prevent the soil from blocking the drainage hole underneath. </p><p>This creates a perfect environment to add whatever plants you like from ones you're propagating, to ones looking for a new home, or perhaps to create entirely new seed starters for spring.</p><div class="product"><a data-dimension112="1c9c8951-38b0-4fc4-9f9d-e36e6829f168" data-action="Deal Block" data-label="If you're specifically looking to create a succulent garden, you may want to invest in some succulent potting mix to place inside your milk jug. This is perfect for indoors and will nourish aloe vera, cacti, echeverias, houseleek, burro's tail and more. Suitably for your jug, this 4 qt. bag will fill up an 8-inch container." data-dimension48="If you're specifically looking to create a succulent garden, you may want to invest in some succulent potting mix to place inside your milk jug. This is perfect for indoors and will nourish aloe vera, cacti, echeverias, houseleek, burro's tail and more. Suitably for your jug, this 4 qt. bag will fill up an 8-inch container." data-dimension25="$16.07" href="https://www.amazon.com/Miracle-Gro-Succulent-Potting-Mix-Fertilized/dp/B08GY7MZMC" 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="3X4XMeWQNPLZmXpeJRhDJA" name="succulent_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/3X4XMeWQNPLZmXpeJRhDJA.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you're specifically looking to create a succulent garden, you may want to invest in some succulent potting mix to place inside your milk jug. This is perfect for indoors and will nourish aloe vera, cacti, echeverias, houseleek, burro's tail and more. Suitably for your jug, this 4 qt. bag will fill up an 8-inch container.<a class="view-deal button" href="https://www.amazon.com/Miracle-Gro-Succulent-Potting-Mix-Fertilized/dp/B08GY7MZMC" target="_blank" rel="nofollow" data-dimension112="1c9c8951-38b0-4fc4-9f9d-e36e6829f168" data-action="Deal Block" data-label="If you're specifically looking to create a succulent garden, you may want to invest in some succulent potting mix to place inside your milk jug. This is perfect for indoors and will nourish aloe vera, cacti, echeverias, houseleek, burro's tail and more. Suitably for your jug, this 4 qt. bag will fill up an 8-inch container." data-dimension48="If you're specifically looking to create a succulent garden, you may want to invest in some succulent potting mix to place inside your milk jug. This is perfect for indoors and will nourish aloe vera, cacti, echeverias, houseleek, burro's tail and more. Suitably for your jug, this 4 qt. bag will fill up an 8-inch container." data-dimension25="$16.07">View Deal</a></p></div><h2 id="here-s-what-happened-when-i-tried-it-6">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="UYeBVmzRSQu7FnH3GpUkD7" name="milk_garden_hack_2" alt="A milk jug being turned into a plant pot" src="https://cdn.mos.cms.futurecdn.net/UYeBVmzRSQu7FnH3GpUkD7.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>I'm always keen to give hacks a try myself before suggesting them, just so I can make sure they actually turn out the way they've been described online. But, while I'm still in the process of propagating my succulents, for now I've only built my miniature garden pot from a milk jug and have yet to add in my plants.</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="p4kE2PR69wDU4cp4V6SoE7" name="milk_garden_hack_3" alt="A milk jug being turned into a plant pot" src="https://cdn.mos.cms.futurecdn.net/p4kE2PR69wDU4cp4V6SoE7.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>I found this hack super simple and for some reason, my milk jug fit perfectly together, meaning I didn't need to use any zip-ties either. I'm mainly impressed with how it looks as I imagined the edges would be jagged from cutting it up. What I will do next though is decorate it to make it look a little more aesthetic among my other pots.</p><p>What I do know for certain is that it cost me absolutely zero dollars to create (apart from the milk, which I'd buy regardless). It also turned out to be a fun project that I could get my six-year-old involved with, too. </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/gardening/this-10-minute-gardening-routine-has-made-a-huge-difference-to-my-outdoor-space-heres-why-i-do-it-every-evening">This 10-minute gardening routine has made a huge difference to my outdoor space — here's why I do it every evening</a></li><li><a href="https://www.tomsguide.com/home/supercharge-your-yard-without-spending-a-dime-5-household-items-to-repurpose-right-now">Supercharge your yard without spending a dime: 5 household items to repurpose right now</a></li><li><a href="https://www.tomsguide.com/home/gardening/this-garden-hack-will-help-your-plants-thrive-and-all-you-need-to-do-is-boil-some-eggs">This garden hack will help your plants thrive — and all you need to do is boil some eggs</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The Narwal Flow impressed me with its performance —but I wish it were easier to use ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/smart-home/narwal-flow-robot-vacuum-and-mop-review</link>
                                                                            <description>
                            <![CDATA[ The Narwal Flow is a great everyday vacuum and an excellent mop, but the user experience can be frustrating, especially in multi-level homes ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ydTSbneKntnpuuV8bkgkoa</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/bVAUbchmfZ5mW3MLojcHFa-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 11 Jun 2026 09:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Smart Home]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                                                                                    <dc:creator><![CDATA[ Amy Cutmore ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/F6RNTkj5fchxaonnvpezAB.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/bVAUbchmfZ5mW3MLojcHFa-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Narwal Flow robot vacuum review]]></media:description>                                                            <media:text><![CDATA[Narwal Flow robot vacuum review]]></media:text>
                                <media:title type="plain"><![CDATA[Narwal Flow robot vacuum review]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/bVAUbchmfZ5mW3MLojcHFa-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">Narwal Flow Robot Vacuum & Mop Review: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Model number: </strong>YJCC026<br><strong>Battery run time: </strong>Approx 190 minutes<br><strong>Cable length: </strong>Approx 55in/140cm<br><strong>Number of settings: </strong>3 (Clean/Mop/Clean & Mop); 4 power options; 4 mopping settings<br><strong>Bin capacity: </strong>2.5ltr dustbag<br><strong>Water tank capacity: </strong>4.6ltrs (clean: 5000ml Dirty: 4750ml)<br><strong>Bagged: </strong>Yes<br><strong>Dimensions:</strong> Robot - W14.3in x D13.8in x H3.7in (W36.4cm x D35.1cm x H9.5cm); Dock H18.1in x W16.9in x D15.8in (H46.1cm x W43cm x D40.2cm)<br><strong>Weight: </strong>Robot - 11.02lb (5kg)/Dock - 22.49lb (10.2kg), excluding plug</p></div></div><p>There’s no doubt in my mind that the <a href="https://www.tomsguide.com/us/best-robot-vacuums,review-2000.html">best robot vacuum cleaners</a> are getting closer in performance to their non-autonomous cousins every year, and I've tested my fair share. </p><p>Narwal might be a less familiar brand to some of you, but it’s one of five big Chinese brands that’s made a huge impact on the robot vacuum market. Narwal in particular has been focused on robots that mop as well as vacuum, and that's exactly what I tested here. </p><p>Over two months, the Narwal Flow primarily lived in the hallway adjoining my dining room and sitting room. With a busy period falling right in the middle of our testing window, it saw a fair bit of action in cleaning up after visitors. That included vacuuming our guest bedroom's low-pile carpet, and mopping our tiled kitchen and downstairs loo, and the vinyl in our bathroom. That's a lot of social pressure, so how did it fare? </p><h3 class="article-body__section" id="section-narwal-flow-robot-vacuum-mop-review-price-and-availability"><span>Narwal Flow Robot Vacuum & Mop 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:1600px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qNQJGbuzGkZuu3kULDJX7U" name="Narwal Flow robot mop and vacuum unboxed" alt="Narwal Flow robot mop and vacuum in living room taken out of wrapping" src="https://cdn.mos.cms.futurecdn.net/qNQJGbuzGkZuu3kULDJX7U.jpg" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future/Amy Cutmore)</span></figcaption></figure><p>The Narwal Flow has an official RRP of $1,599.99 and is currently available on the <a href="https://us.narwal.com/products/flow-robot-vacuum-and-mop" target="_blank" rel="nofollow">US Narwal site for $1,399.99</a>and at <a href="https://www.amazon.com/NARWAL-Real-Time-Self-Cleaning-Dual-Camera-Self-Emptying/dp/B0F6CWKBTN" target="_blank" rel="nofollow">Amazon for $1,499</a></p><p>UK users can purchase this on Amazon or on the <a href="https://narwal-uk.co.uk/products/narwal-flow-robot-vacuum-mop-compact-dock" target="_blank" rel="nofollow">Narwal UK website for £1,330</a>. </p><h3 class="article-body__section" id="section-narwal-flow-robot-vacuum-mop-review-design"><span>Narwal Flow Robot Vacuum & Mop Review: Design</span></h3><p>The first thing that strikes you when the Flow arrives is its heft. It comes in a huge box that is marked as heavy – I'd suggest you heed the warning and have two people lift it. </p><p>Open the box from the top and there is a cardboard sleeve containing the rather sparse instruction manual (there are just five pages of instructions in English) and the two spinning brushes that clip into the base of the vacuum. </p><p>Lift this off and the vacuum and its docking station are held in place with a cardboard support. The vacuum is already housed in its base (with some polystyrene for good measure), and both are wrapped in a plastic bag. </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="QLCtJpXEATdKrhHJRupW4P" name="Narwal Flow Robot Vacuum & Mop in box in living room" alt="Narwal Flow Robot Vacuum & Mop in box in living room" src="https://cdn.mos.cms.futurecdn.net/QLCtJpXEATdKrhHJRupW4P.jpg" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">The robot is mostly packaged in recyclable cardboard, though there are a few plastic bags in there, too.  </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future/Amy Cutmore)</span></figcaption></figure><p>Open the lid of the dock and you'll find two water tanks – one housing some pleasant surprises. First there's a spare dust bag (one was already fitted inside the dock), as well as a bottle of detergent, a spare vacuum filter (again, one is already fitted) and a spare 'insert bin' – the small bin that sits inside the vacuum itself and is emptied after every visit to the docking station. </p><p>It's nice to have these replaceable parts included, especially as it's probably the first time most of us will have bought a Narwal product. New <a href="https://us.narwal.com/products/base-station-dust-bag-for-freo-z-ultra" target="_blank" rel="nofollow">dustbags will set you back $19.99 for two on Narwal's website, </a> while a new filter costs $35.99, again for two. Narwal says a dustbag will typically last 120 before it needs replacing.</p><p>I was, however, a little disappointed that there were no spare side brushes at this price point. In my experience of robot vacuums, I've found these need replacing reasonably frequently. That said, Narwal's have survived two months of testing very well. </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="AVKtEh8AMGuLUHHmKE527T" name="Narwal Flow robot mop and vacuum spare parts and clean water tank" alt="Narwal Flow robot mop and vacuum spare parts and clean water tank" src="https://cdn.mos.cms.futurecdn.net/AVKtEh8AMGuLUHHmKE527T.jpg" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class=""></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">I was impressed that the Narwal Flow came with an array of replacement parts to get you started. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future/Amy Cutmore)</span></figcaption></figure><p>The Flow's vacuum design is pleasantly simple. It has an all-over white, slightly glossy finish, which I preferred over black – I find the latter tends to show up even the tiniest amount of settled dust quite badly. </p><p>Underneath, there's a mop pad that lifts by 0.47in/1.2cm when not in use so that you won't get carpets or wood floors damp, and shuffles left and right to get tight up against edges. <br><br>There are also those two side brushes that easily clip into place and can shift from a V formation to a "II" one and reverse direction so that nothing gets tangled in them. <br><br>Finally, there is a tangle-free roller brush, which rather ingeniously connects on one side only. This is meant to help hair to flow cleanly into the dustbin – and as I'll explain, worked brilliantly. </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="Jzhsg5UkcWmJb3Pxd7wwx5" name="Narwal Flow upside down showing brushes and mop" alt="Narwal Flow upside down showing brushes and mop" src="https://cdn.mos.cms.futurecdn.net/Jzhsg5UkcWmJb3Pxd7wwx5.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: Future/Amy Cutmore)</span></figcaption></figure><p>Initially, setting everything up seemed straightforward. The only extra bits I needed to attach were the two spinning brushes, which clipped into place easily. Everything else – the mop pad, the filter tray in the base of the docking station, dust bag, mini vac bin and water tanks were already in position. </p><p>As explained, I popped the the vacuum in my narrow hallway. Now, Narwal recommends leaving a 0.45m long space in front of dock so that the Flow can easily manoeuvre in and out. I only had a 34in/88cm-wide space to play with, but the robot seemed to take this in its stride. It is a bulky unit though, so you will need to think carefully about where it might live without it becoming a nuisance. Also, the 1.4m power cable will limit you in terms of nearby power sockets. </p><p>As the five-page instruction manual wasn't much help, I was eager to download the app and pair my robot to WiFi. Both processes were easy, I entered my router password, and the Flow was online in a handful of seconds. So far, so good — but then I had to deal with the app. </p><h2 id="narwal-flow-robot-vacuum-mop-review-app-experience">Narwal Flow Robot Vacuum & Mop Review: App Experience</h2><p>When you first open the app, you'll be asked to select your model of Narwal, agree to terms and conditions, and then you'll be prompted to link to your WIFI. </p><p>Then, you'll be directed to set up an initial map for your robot. I did this without really thinking, assuming it would be easy to change later. But I suggest you pay close attention and prepare your main cleaning area beforehand. Otherwise, it gets a bit fiddly to change things, and the vacuum might take a while to respond.<br><br>Clear the floor of obstacles, open doors where you want the vacuum to travel between rooms and close doors to doors you don't want it to clean. Also, be sure that the cleaning cycle is set to vacuum only. Otherwise you risk it mopping carpets, wood floors and other things that really shouldn't be wet.</p><p>I made the mistake of not properly clearing my floor, and it took a few days for the vacuum to realise where it was allowed to go.</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="HtdUQWotRrhBS9RcpmSxdM" name="Narwal Flow screen grabs" alt="Narwal Flow app screen grabs" src="https://cdn.mos.cms.futurecdn.net/HtdUQWotRrhBS9RcpmSxdM.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>Unlike the far less expensive Roborock Q7 M5 vacuum, the Narwal also didn't pick up on my kitchen in its initial mapping. While it mapped the dining room, hallway, and living room area reasonably successfully (albeit not picking up the correct position of the sofa), it didn't seem to 'see' the kitchen beyond, even though I took a risk and left the door to it open.</p><p>On the first pass it simply ignored the space. Then, on the second clean, it actually threw itself down the kitchen step in a dramatic move that left it upended and crying for help. So I wouldn't put total faith in its edge detection. </p><p>But one thing I did love about the Narwal Flow was its smarts when it came to detecting the surface below. It doesn't clean during its initial mapping so there's no danger of it trying to mop a rug, for example. But it was quick to spot surface materials (wood, tiles, rugs, carpet) and committed them to memory almost immediately. And while I have seen other reviews saying that the Narwal accidentally mopped carpet on occasion, I had no issues. </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="CPjTU8itCsRqVhcXdwnk3b" name="NARWAL FLOW SCHEDULE CLEANING APP SCREENGRABS" alt="NARWAL FLOW SCHEDULE CLEANING APP SCREENGRABS" src="https://cdn.mos.cms.futurecdn.net/CPjTU8itCsRqVhcXdwnk3b.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><strong>Scheduling cleaning in the app</strong><br><br>Scheduled cleaning is also relatively easy to add and set up. You'll first need to select whether you want the Narwal Flow to Vacuum and Mop, Vacuum then Mop, Vacuum or Mop Next, you hit schedule (bottom left) and pick the next date you want it to clean. From here, you'll be able to set the time, date and frequency of when it cleans (you have options of every 1-6 days or 1-4 weeks), as well as the rooms to clean. <br><br>While you can't set it to do different tasks in different parts of the house on one schedule, you can set multiple schedules focused on different tasks.<br><br>I found this all really easy to do, and the Narwal Flow loyally followed my instructions each 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TjTNQpzT9zjUdNGWwutexb" name="Narwal Flow app settings screen grabs" alt="Narwal Flow app settings screen grabs" src="https://cdn.mos.cms.futurecdn.net/TjTNQpzT9zjUdNGWwutexb.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><strong>Other app features</strong></p><p>There's also a 'history' section where you can see how much cleaning your robot has done in the past day, week or year, including the area cleaned and time taken. To be honest, I'm not sure how useful people will generally find this, bar resolving a family dispute about whether the housework has been done!</p><p>There are two other key areas of the app worth pointing out. The 'Dock' icon on the bar to the left of the map will take you to a handy screen that lets you know if your dust bag needs emptying. And the hexagon on the bar to the top right takes you to various settings such as the language, voice (male or female) and volume of the voice, or pet-friendly mode, which you can activate to make sure the robot is extra-cautious in case it runs into any furry friends. This also does clever things like enable a 'pet cleaning zone' that will up the intensity of cleaning in certain areas. <br><br>My general feelings about the app were that it was a little slow, with some lag. A lot of functions were hard to find, such us the child lock (under general), and I found the map management quite fiddly. I did get used to it after a couple of months. But its worth spending some time to familiarise yourself with all the settings. </p><h3 class="article-body__section" id="section-narwal-flow-robot-vacuum-mop-review-performance"><span>Narwal Flow Robot Vacuum & Mop Review: Performance</span></h3><h2 id="what-s-it-like-to-use-on-hard-floors">What's it like to use on hard floors?</h2><p>Initially, I gave the Narwal Flow access to our hallway, dining room, and living room, leaving the door to the kitchen open to see if it would acknowledge the step down to this area. All three have solid wooden floors.<br><br>I also used the vacuum regularly on my tiled kitchen floor and in my lino-covered bathroom. The robot worked in a logical way, first working its way around the perimeter of the room, before crossing up and down. It felt that no spot was missed, and lots of dirt and debris was captured. I did leave a sprinkling of small debris along with things I didn't want it to pick up – including a Lego Minifigure. Impressively, my Lego friend was avoided while other scraps of paper were collected.     <br><br>However, on close inspection, the edge cleaning wasn't exceptional. The Narwal Flow struggled to clean the dust that gathered under my radiators, and also when picking up dirt along the kitchen skirting boards. It wasn't just an issue of tight corners – it wasn't great along long edges either. On more than one occasion, I manually used a dustpan brush to sweep out debris from tighter corners to the main floor, where it was then successfully picked up. </p><p>I did like, however, how it avoided obstacles. As you are cleaning, if it encounters any, it will take a picture of what its seen. This then appears as an icon on the cleaning map, and if you click through, you'll see what it avoided. </p><p>And unlike other robot vacuums I tried, the Narwal Flow has less of a tendency to get stuck, thanks to its low profile and impressive navigation. Unlike the <a href="https://www.tomsguide.com/home/smart-home/roborock-q7-m5-robot-vacuum-review">Roborock Q7 M5</a> it was able to glide under my sofa and TV bench with ease, and tucked under radiators, even if it didn't actually clean those edges very well. </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="y9ujjFhukUuJBMFMEe56YM" name="Narwal cleaning under TV bench" alt="Narwal Flow cleaning under TV bench" src="https://cdn.mos.cms.futurecdn.net/y9ujjFhukUuJBMFMEe56YM.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/Amy Cutmore)</span></figcaption></figure><p>Next, let's focus on the noises it makes. I've come to realise that most robot vacuum cleaners like to talk, and the Narwal Flow is no exception. As soon as 'she' (there is an option for a male voice if you prefer, but the default is female) is prompted to clean via the app or the button on the top of the dock, she will tell you 'vacuuming starting'. When she's done, she returns to base and says 'self cleaning started', then 'charging started'. There's lots of other chat in her repertoire, too, depending on whether she's mopping, or just plain stuck.<br><br>One thing the Narwal Flow has over some other robots though, is that it responds to voice commands. Say "Hey Nawa, start cleaning the living room" and it will. I also asked it to "stop cleaning", "go back to base" and "adjust suction power to super powerful" and all instructions were obeyed. It also reassuringly says 'I'm here' whenever you command it, so you know it's listening.</p><p>There is a little trial and error involved. If you haven't set room names in the Map Editing area, it's not automatically going to know what you mean by 'vacuum the kitchen'. And it didn't respond to 'return to dock' or 'reduce suction' – you do need to be quite specific. But once you've mastered the specific commands, the voice control is really useful. Especially if you have your hands full and want to clean a spill quickly without reaching for your phone. </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="kqkcSv2yTPgMqwfe3hJqT" name="Narwal Flow in Hallway overhead" alt="Narwal Flow in Hallway overhead" src="https://cdn.mos.cms.futurecdn.net/kqkcSv2yTPgMqwfe3hJqT.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/Amy Cutmore)</span></figcaption></figure><p>As more of a scientific test of its cleaning abilities, we cleared a 5ft x 5ft area of both a tiled and carpeted floor, and asked the Narwal Flow to pick up 20g each of Cheerios, kitty litter and a mix of dog and human hair. The results were as follows</p><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p>Overall/average</p></td><td  ><p>Cheerios </p></td><td  ><p>Kitty litter</p></td><td  ><p>Pet/human hair</p></td></tr><tr><td class="firstcol " ><p>Tiled floor</p></td><td  ><p>83%</p></td><td  ><p>90%/18g</p></td><td  ><p>95%/19g</p></td><td  ><p>70%/14g</p></td></tr><tr><td class="firstcol " ><p>Carpet</p></td><td  ><p>77%</p></td><td  ><p>75%/15g</p></td><td  ><p>80%/16g</p></td><td  ><p>75%/154g</p></td></tr></tbody></table></div><p>In general, the Narwal Flow seems great at picking up ground-in dirt, but seemed to struggle with larger pieces of debris. </p><p>I was quite surprised that when I weighed things out, it actually picked up a larger volume of kitty litter than it did Cheerios on both carpet and hard floors. Instead, it seemed to push these items towards the edges of the room, where they weren't always collected. Increasing the suction to 'super powerful' did help, but cereal was still left behind. <br><br>The same went for the small clumps of hair (a mix of human, pet and artificial), that I littered the floor with. The Narwal didn't always do the best job of picking them up. At one point it did wonder if it was actively avoiding them! However, the hair it did pick up generally got sucked into the dust bin and little got caught in the brush roller which I was super impressed with. </p><h2 id="noise-levels-2">Noise levels</h2><p>Using a sound meter app, I measured the average volume of the Narwal Flow while standing around 1m away in these different scenarios:</p><p><strong>Cleaning wood floors: </strong>55dB (Quiet) to 74dB (Super powerful)<br><strong>Cleaning carpet: </strong>49dB (Quiet) to 68dB (Super powerful)<br><strong>Cleaning ceramic floor tiles: </strong>55dB (Quiet) to 71.5dB (Super powerful)</p><p>The moral is, when the Narwal is quiet, it's very quiet, and when it's loud, you don't really want to be in the same room. That said, the noise really does convince you of its suction power!</p><h2 id="what-is-it-like-to-use-on-carpet">What is it like to use on carpet?</h2><p>Anyone that read my review of the Roborock Q7 M5 will know that I was pleasantly surprised regarding its performance on carpet, but the Narwal Flow took things to another level when it came to extracting dirt and dust. </p><p>The picture below shows the nastiness extracted from the same bedroom carpet that had been vacuumed two weeks before (yikes). I was in equal parts impressed and horrified... </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="o2Cj3bZMj9qETs2xEnYUwg" name="Narwal Flow dust compartment contents" alt="Narwal Flow dust compartment contents" src="https://cdn.mos.cms.futurecdn.net/o2Cj3bZMj9qETs2xEnYUwg.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>As the Cheerios/kitty litter/hair testing data shows, it was marginally better at picking up hair on carpet compared to hard floors. Otherwise, the vacuum performed better on hard floors when it came to picking up cereal and grit.  </p><p>This was good news in a way, as hair tends to collect on my bedroom carpet, as it's where I blow-dry and style my 'do. I don't tend to eat or wear shoes in the carpeted parts of our home, so I wasn't too concerned about its performance in the other tests and would still highly recommend it based on the dirt I saw it remove.</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="aVwbtjJEgoG8kCmgNZacaC" name="Narwal Flow avoiding Lego figure" alt="Narwal Flow avoiding Lego figure on carpet" src="https://cdn.mos.cms.futurecdn.net/aVwbtjJEgoG8kCmgNZacaC.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/Amy Cutmore)</span></figcaption></figure><h2 id="what-is-it-like-to-use-as-a-mop">What is it like to use as a mop?</h2><p>If there is one good reason to buy the Narwal Flow, it's that it is brilliant at cleaning floors. I was actually amazed at the job it did – in fact, it actually beat out a plug-in Bissell steam cleaner when it came to cleaning up dried-on ketchup. </p><p>The only suitable floor to test the Narwal's mopping ability on was our kitchen, which meant relocating the dock down a step to the kitchen floor. This is because the Narwal will periodically return to the dock to empty any dirty water and pick up some fresh water-detergent mix. As a task, this was cumbersome, but if your hard floors are all on one level, it won't be an issue. </p><p>The Narwal's base station has two water tanks – one purple, for your clean water and detergent, and one for dirty water. Narwal recommends adding one to three capfuls of detergent for every full tank of water, which I measured to be approx 4.6 litres using a jug. In our tests, we decided to go for two capfuls – I'd imagine the bottle would last as long as 6-8 tankfuls of water, and given I'd use less than half a tank for each wash, that's at least 12-16 cleans of my kitchen. <br><br>Narwal does recommend you only use its own cleaning solution in the tank 'otherwise the device may become faulty', according to the instruction manual. </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="gMCwDwFAk45M9NWScarEgN" name="Narwal Flow water tank and detergent" alt="Narwal Flow water tank and detergent on kitchen counter with jug" src="https://cdn.mos.cms.futurecdn.net/gMCwDwFAk45M9NWScarEgN.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/Amy Cutmore)</span></figcaption></figure><p>Once you have added the water and detergent you simply reload the clean water tank, and command the robot to mop. It takes a few minutes for the tank to heat up the water, and you'll hear a lot of gurgling as it does so. It then works around the room, again starting at the perimeters and then working back and forth, up and down. <br><br>The water that comes out of the mop steams hot, and leaves the floor reassuringly  warm and slightly damp. I was impressed that there were never any puddles of water on the floor, which can often be left by the Bissell cleaner I'm currently testing, and that this careful release of water cleaner effectively yet dried 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="SkUXuzYjSaxbdbv7Hp9wGM" name="Narwal Flow cleaning kitchen floor" alt="Narwal Flow cleaning kitchen floor" src="https://cdn.mos.cms.futurecdn.net/SkUXuzYjSaxbdbv7Hp9wGM.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/Amy Cutmore)</span></figcaption></figure><p>The Narwal Flow will repeatedly run the mopping cycle until it deems the floor to be totally clean. In the case of the kitchen, it ran four cycles, going back to the base each time to reload with fresh water. My kitchen measures approximately 6 sq m (2 x 3m), and the whole process took 27 minutes. <br><br>Beforehand, I deliberately left some ketchup to dry overnight on the tiles, and you can see the results below. The Narwal cleaned the mess almost instantly – going over the area twice. But I was impressed at how quickly the ketchup lifted from the surface. <br><br>You can also see the dirty water collected by the Narwal at the end of the cleaning cycle. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/DFR5Ththp5Zd7wo7Vh3wCE.jpg" alt="NARWAL FLOW ketchup stain on floor before" /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/XijF7PdJNDxTefRqyD4TgH.jpg" alt="Narwal Flow ketchup stain on floor after" /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/JGYuydbdYEkpYJkuq49egE.jpg" alt="Narwal Flow water tanks with clean and dirty water" /><figcaption><small role="credit">Future/Amy Cutmore</small></figcaption></figure></figure><h3 class="article-body__section" id="section-narwal-flow-robot-vacuum-mop-review-ease-of-cleaning-and-maintenance"><span>Narwal Flow Robot Vacuum & Mop Review: Ease of cleaning and maintenance</span></h3><p>In the main, the Narwal Flow is very easy to look after. The large water tanks are very easy to wipe clean, with few awkward nooks and crannies, and the dust mag is extremely simple to unclip and replace. </p><p>Usually the bane of my life when it comes to vacuums, the brush roller on the Narwal is one of the best I've ever encountered. To remove it, you simply press down on a purple catch and push in and up to unclip the cover. As the roller is only secured to one side of the main body of the vacuum, it's then just a case of pulling it out. As the roller is slightly tapered, any hair naturally works its way down the roller and gets sucked into the dust cavity. I would occasionally have to remove hair that had been tightly wrapped, but this happened rarely and was easy to pull free without scissors due to the design. </p><p>My one major bugbear is the two side brushes. Once clipped in, there is no obvious way to remove them. For now, that's not an issue, but if you did want to replace them down the line, I'm not sure how you'd do so.  </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="udctNs6qbLYFAUo5DpzDwZ" name="Narwal Flow on wood floor" alt="Narwal Flow on wood floor next to plants" src="https://cdn.mos.cms.futurecdn.net/udctNs6qbLYFAUo5DpzDwZ.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/Amy Cutmore)</span></figcaption></figure><h3 class="article-body__section" id="section-narwal-flow-robot-vacuum-mop-review-comparisons"><span>Narwal Flow Robot Vacuum & Mop Review: Comparisons</span></h3><p>As mentioned, I have recently tested the more basic spec Roborock Q7 M5, which doesn't have the self-emptying function, and costs a fraction of the price. In my opinion, the Roborock is superior to the Narwal Flow in its suction power and ability to pick up larger pieces of debris, and I also found its app and mapping easier to use and navigate. And the fact the Q7 M5 takes up a lot less space will be a boon to many. </p><p>However, if you are looking for a vacuum-mop hybrid, the Narwal Flow wins hands down. Its mopping ability is simply superb. And the Narwal is also more suited to allergy sufferers, since all the dust is collected up into a bag – as long as you're happy to pay extra for the dust bags. </p><p>I will also give the Narwal some credit for its voice control feature, which works well once you've mastered the commands. And I did like certain aspects of the app – for example, that it takes pictures of any obstacles it encounters so you can see what's causing issues from your phone. It's one of those little details that might no be particularly necessary, but is still clever. And imagine if you have pets you'll really appreciate it!</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="GwUD5JANHsX2DiDz8tDTrR" name="Narwal Flow in Hallway" alt="Narwal Flow in Hallway" src="https://cdn.mos.cms.futurecdn.net/GwUD5JANHsX2DiDz8tDTrR.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><h3 class="article-body__section" id="section-narwal-flow-robot-vacuum-mop-review-verdict"><span>Narwal Flow Robot Vacuum & Mop Review: Verdict</span></h3><p>There is a lot to like about the Narwal Flow. Top of the list are its exceptional mopping capabilities and easy maintenance. It's also on the quiet side for a robot vacuum, particularly on carpet, where its powerful suction pulls out deep-seated dirt with ease. And while it sometimes struggles with larger clumps of hair, it's unlikely you'll find them tangled in the brush roller for once!</p><p>I found the Flow less successful at edge cleaning and picking up very large particles such as cereal. My other main bugbear is that if you want to use it in other rooms that aren't on the same level as the base station, it can be temperamental or unwilling to co-operate.<br><br>Be aware that replacement dustbins and cleaning solution are quite pricey, so ongoing usage may be more costly. And the app take some getting used to. I did like the easy scheduling though, and was impressed by its AI smarts, such as floor material detection. </p><p>However, I'd recommend the Narwal Flow for homes with large living areas on one level and for those that prize good mopping skills. It's definitely worth considering.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Why is my tomato plant wilting? 5 common causes and how to fix it ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/why-is-my-tomato-plant-wilting-5-common-causes-and-how-to-fix-it</link>
                                                                            <description>
                            <![CDATA[ Learn why tomato plants wilt and how to revive them before permanent damage occurs. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">4zhRCbtxZsYcocitZmysbX</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/hT6R52gwRLPXtSDhcEyj27-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 11 Jun 2026 06:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                <author><![CDATA[ kaycee.hill@futurenet.com (Kaycee Hill) ]]></author>                    <dc:creator><![CDATA[ Kaycee Hill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xHn6RmpEqg87cvtLwrBu9G.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/hT6R52gwRLPXtSDhcEyj27-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Wilting tomato plant ]]></media:description>                                                            <media:text><![CDATA[Wilting tomato plant ]]></media:text>
                                <media:title type="plain"><![CDATA[Wilting tomato plant ]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/hT6R52gwRLPXtSDhcEyj27-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Don't ignore a drooping tomato plant. When heat hits and your prized tomatoes suddenly sag, they’re throwing up a red flag. It’s a heartbreaking sight, but that dramatic slouch is actually a cry for help.</p><p>Wilting is a copycat symptom for five completely distinct issues, from simple thirst to hidden pests. Guessing the cure can cost you your harvest, and applying the wrong fix could finish the plant off entirely.</p><p>Fortunately, most tomato drama is completely reversible once you know what to look for. Here's 5 common reasons your tomato plant wilts and how to fix fast. </p><section class="howto-block">                    <h3>1. Underwatering</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/nv32zGerpRogbc7ejDpr3Q.jpg"                                        alt="Wilting tomato plant"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/nv32zGerpRogbc7ejDpr3Q.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Tomatoes are thirsty plants that need 1-2 inches of water per week. During hot, dry summers, they need even more. If you're watering on a schedule that made sense in spring, it probably isn't enough now that temperatures have risen.</p><p>Check the soil 1-2 inches below the surface.<strong> If it feels completely dry, underwatering is the problem</strong>. Look at the leaves too. <strong>Dry, papery leaves confirm the plant isn't getting enough water</strong>.</p><p>Increase watering frequency rather than dumping more water at once. Water more often with smaller amounts so the soil stays consistently moist without becoming soggy. </p></p>                </section><section class="howto-block">                    <h3>2. Overwatering</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/srZrJgdQryVds4dhu928kZ.jpg"                                        alt="Watering tomato plants"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/srZrJgdQryVds4dhu928kZ.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Too much water, on the other hand, causes wilting that looks identical to underwatering. The difference is in the details.<strong> Overwatered plants have thick, soft leaves instead of dry, papery ones, and the soil stays damp days after watering</strong>.</p><p>Overwatering promotes fungal growth and root rot. Roots suffocate in waterlogged soil and can't absorb water properly, which ironically causes the plant to wilt despite sitting in wet soil.</p><p>Stop watering until the top 1-2 inches of soil feel dry. When you do water, apply it slowly and let the soil dry between waterings rather than keeping it constantly soggy. If recent heavy rain is the culprit, just let the soil dry out naturally and resume normal watering once it does.</p></p>                </section><section class="howto-block">                    <h3>3. Root damage</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/72GfUBMARtYte2BtgFAD3e.jpg"                                        alt="Transplanting tomato plant outdoors"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/72GfUBMARtYte2BtgFAD3e.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Moving tomatoes from containers to garden beds often damages roots. Small root breaks mean fewer roots absorbing water and nutrients. The plant wilts in the first few days after transplanting even if you're watering correctly.</p><p>The plant will naturally regrow damaged roots without any intervention from you. <strong>Give it time and continue providing proper water and nutrition</strong>. Most plants recover within a few days as new roots develop.</p><p>If wilting persists beyond a week after transplanting, something else is causing the problem. Consider one of the other reasons on this list.</p></p>                </section><section class="howto-block">                    <h3>4. Pests </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/wYN9D5BJ9W2zMUbbNvdfpZ.jpg"                                        alt="Aphids crawling on tomato plant leaves"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/wYN9D5BJ9W2zMUbbNvdfpZ.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Sap-sucking insects like aphids, spider mites, and whiteflies drain vital fluids directly from tomato leaves, causing sudden wilting, curling, and eventual plant death. </p><p>Because these pests hide on leaf undersides, regular inspection is crucial — <strong>look closely for tiny bugs, sticky honeydew, or fine webbing</strong>. If your plants are actively wilting from a severe infestation, thoroughly coat all leaf surfaces with insecticidal soap every 5–7 days until they disappear. </p><p>You can also prevent this fluid loss naturally by interplanting basil, mint, or <a href="https://www.tomsguide.com/home/gardening/why-marigolds-are-the-best-companion-flower-for-a-pest-free-vegetable-garden-especially-tomatoes">marigolds</a>, around your garden borders to deter pests early. </p><p>Protect your plant's natural defense system by avoiding broad-spectrum pesticides, which accidentally wipe out the ladybugs and lacewings that keep these wilting-inducing pests in check.</p></p>                </section><section class="howto-block">                    <h3>5. Over-pruning</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/6eBgFFXKVap2akC9qi3Y9A.jpg"                                        alt="Pruning tomato suckers"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/6eBgFFXKVap2akC9qi3Y9A.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Pruning directs plant energy toward fruit instead of extra leaves. But aggressive pruning causes stress that makes plants wilt. If your tomato started drooping days after a heavy pruning session, stress is the cause.</p><p>Let the plant recover before pruning again. <strong>Focus future pruning on removing only the suckers between main and lateral stems rather than cutting back entire branches</strong>. Pinch these small shoots off with your fingers instead of using pruning shears for more aggressive cuts.</p><p>The plant will bounce back once it's had time to recover. Resume normal care and wait for signs of new growth before considering another pruning session.</p></p>                </section><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XZQZ5O"></div>                            </div>                            <script src="https://kwizly.com/embed/XZQZ5O.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/home/gardening/why-are-my-tomato-plant-leaves-turning-yellow-5-steps-to-save-your-harvest">Why are my tomato plant leaves turning yellow? 5 steps to save your harvest</a></li><li><a href="https://www.tomsguide.com/home/gardening/why-are-my-tomato-plant-leaves-turning-yellow-5-steps-to-save-your-harvest-clone">How to double your tomato harvest — the 3-step pruning system for massive yields</a></li><li><a href="https://www.tomsguide.com/home/outdoors/gardeners-swear-by-this-simple-trick-heres-how-to-save-split-tomatoes-fast">Gardeners swear by this simple trick — here's how to save split tomatoes fast</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Going camping this summer? These are the 12 affordable IKEA buys you didn't know you needed ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/going-camping-this-summer-these-are-the-12-affordable-ikea-buys-you-didnt-know-you-needed</link>
                                                                            <description>
                            <![CDATA[ If you're planning a camping trip this summer, these IKEA budget finds will get you kitted out ready for your next big adventure. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">Qo8WJTQ5TFY2s8smjGTKJ6</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/cmRnbRXzsjNuhUtgGf63FY-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 10 Jun 2026 13:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></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/cmRnbRXzsjNuhUtgGf63FY-1280-80.jpg">
                                                            <media:credit><![CDATA[IKEA / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of IKEA camping products.]]></media:description>                                                            <media:text><![CDATA[A selection of IKEA camping products.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of IKEA camping products.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/cmRnbRXzsjNuhUtgGf63FY-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I'm a big fan of camping. My dad used to take me when I was little. I've done it at festivals and I've even done it in random fields (legally, of course). Over the years, I've gone from roughing it with just a sleeping bag and a prayer for good weather to cultivating a collection of essentials that make the adventure a little more comfortable.</p><p>And while IKEA doesn't have a dedicated camping range, as such, it's turned into one of my go-to places for grabbing gear. With a variety of outdoor furniture, accessories, and even apparel, it's a budget-friendly way to enjoy a summer of tents and backpacking. </p><p>IKEA has even launched the SOLUPPGÅNG range – an outdoor collection that "encourages everyone to enjoy nature without a threshold." A thoughtful musing, but what's even better is it's on an IKEA budget. Here are the 12 IKEA camping essentials I recommend.</p>        <div class="featured_product_block featured_block_hero" data-id="b051b517-f1fd-4d18-af83-4b9d2e2b6c36">            <a href="https://www.ikea.com/us/en/p/rensare-waterproof-bag-30497739/" data-model-name="RENSARE Waterproof Bag" 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/oy6GMJHudWaWXrsE4afBC6.jpg" alt="Rensare Waterproof Bag 6 ¼x4 ¾x9 ½ "/85 Oz"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">RENSARE Waterproof Bag</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="478de98e-1136-49b0-88fd-b0e822b02a7a">            <a href="https://www.ikea.com/us/en/p/ikea-365-water-bottle-dark-gray-20480013/" data-model-name="IKEA 365+ Water Bottle" 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/v9ykg53PAi4wmq5aakn9s9.jpg" alt="Ikea 365+ Water Bottle - Dark Gray 17 Oz"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">IKEA 365+ Water Bottle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="e836783e-192c-48c9-917a-6823e4b1a3e3">            <a href="https://www.ikea.com/us/en/p/soluppgang-mug-enamelled-steel-10621315/" data-model-name="SOLUPPGÅNG Mug" 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/9meX94pbDfSj62Yh9BCvBD.jpg" alt="SoluppgÅng Mug - Enamelled Steel"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">SOLUPPGÅNG Mug</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="536b1b48-813a-4b81-8f25-794bf99bc710">            <a href="https://www.ikea.com/us/en/p/solvinden-led-pendant-lamp-beige-battery-operated-outdoor-globe-50614657/" data-model-name="SOLVINDEN LED Pendant Lamp" 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/snC76BCPjrrwXVBKH4HoZG.jpg" alt="Solvinden Led Pendant Lamp - Beige Battery Operated/outdoor Globe 4 ""></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">SOLVINDEN LED Pendant Lamp</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="e94c15bb-c4e2-4b21-9129-df76a9186859">            <a href="https://www.ikea.com/us/en/p/soluppgang-hat-off-white-80619776/" data-model-name="SOLUPPGÅNG Hat" 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/fi5E2oEmQtbT45jD4dx4oJ.jpg" alt="SoluppgÅng Hat - Off-White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">SOLUPPGÅNG Hat</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="26b06026-de78-4b05-9fb3-99eb641f4677">            <a href="https://www.ikea.com/us/en/p/marulk-21-piece-picnic-set-mixed-colors-50594503/" data-model-name="MARULK 21-Piece Picnic 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/9hbu8n8MnWwGoCu386mcuV.jpg" alt="Marulk 21-Piece Picnic Set - Mixed Colors"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">MARULK 21-Piece Picnic Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="d35d1a4f-43cf-4dfb-83be-d76f89ca3add">            <a href="https://www.ikea.com/us/en/p/soluppgang-led-lantern-outdoor-battery-operated-dimmable-40621286/" data-model-name="SOLUPPGÅNG LED Lantern" 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/E28ZkduRvmiHX45xeeLsQY.jpg" alt="SoluppgÅng Led Lantern - Outdoor Battery Operated/dimmable"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">SOLUPPGÅNG LED Lantern</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="fb46303e-1c13-426d-8cf6-db5ebfc752d4">            <a href="https://www.ikea.com/us/en/p/klotpil-cooler-bag-90586370/" data-model-name="KLOTPIL Cooler Bag" 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/eVbKitBVhWivF3J7LScBHc.jpg" alt="Klotpil Cooler Bag 15x9 ½x15 ¾ ""></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">KLOTPIL Cooler Bag</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="66eaff5e-3844-4fa1-89c8-481e3b5d957b">            <a href="https://www.ikea.com/us/en/p/soluppgang-picnic-blanket-brown-70619852/" data-model-name="SOLUPPGÅNG Picnic Blanket" 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/S6mTJm5HnHfiEENRFRWHwj.jpg" alt="SoluppgÅng Picnic Blanket - Brown 51 ¼x67 ""></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">SOLUPPGÅNG Picnic Blanket</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="d147c888-5dc8-4673-8a9f-4f32dd0c8da6">            <a href="https://www.ikea.com/us/en/p/strandoen-folding-table-bright-yellow-00600460/" data-model-name="STRANDÖN Folding Table" 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/jZMpG3d5N7NkthsD6GEXQo.jpg" alt="StrandÖn Folding Table - Bright Yellow 27 1/2x21 5/8 ""></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">STRANDÖN Folding Table</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4bcf2917-9360-47ae-a1ee-00291eb58d0e">            <a href="https://www.ikea.com/us/en/p/naemmaroe-stool-outdoor-foldable-light-brown-stained-70510300/" data-model-name="NÄMMARÖ Stool" 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/Ebr6CjFDsh2r4sFf5ZCtgm.jpg" alt="NÄmmarÖ Stool, Outdoor - Foldable/light Brown Stained 14 3/8x17 3/4 ""></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">NÄMMARÖ Stool</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="737c9851-4502-480e-a0e7-5af6d24c349e">            <a href="https://www.ikea.com/us/en/p/soluppgang-grill-cast-iron-00621306/" data-model-name="SOLUPPGÅNG Cast Iron Grill" 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/LpZfHkWbQH8xGdaKQHv4r4.jpg" alt="SoluppgÅng Grill - Cast Iron 8 1/4x16 7/8 ""></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>IKEA</div>                                        <div class="featured__title">SOLUPPGÅNG Cast Iron Grill</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/home/smart-home/ikeas-new-wireless-charger-looks-like-a-donut-and-costs-usd9-but-its-surprisingly-capable">IKEA's new wireless charger looks like a donut and costs $9, but it's surprisingly capable</a></li><li><a href="https://www.tomsguide.com/home/i-organize-my-home-with-these-ikea-storage-items-and-its-never-looked-better">I just upgraded my home with these 5 IKEA items</a></li><li><a href="https://www.tomsguide.com/home/how-to-paint-your-ikea-furniture-for-a-flawless-finish-decorating-experts-share-their-top-tips">How to paint your IKEA furniture for a flawless finish — decorating experts share their top tips</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Roborock's new robot lawnmower has some incredibly smart features — but here's one I'm really excited about ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/roborocks-new-robot-lawnmower-has-some-incredibly-smart-features-but-heres-one-im-really-excited-about</link>
                                                                            <description>
                            <![CDATA[ The Roborock RockNeo Q110H makes smart lawn care accessible and effortless – and it's just become available to buy exclusively in the US. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">s3rXCDjwr7gZkpm4YXrhKf</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/ejm9uPmhYwEQPekQPHEyrA-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 10 Jun 2026 10:18:14 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></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/ejm9uPmhYwEQPekQPHEyrA-1280-80.jpg">
                                                            <media:credit><![CDATA[Roborock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Roborock RockNeo Q110H in the garden]]></media:description>                                                            <media:text><![CDATA[The Roborock RockNeo Q110H in the garden]]></media:text>
                                <media:title type="plain"><![CDATA[The Roborock RockNeo Q110H in the garden]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/ejm9uPmhYwEQPekQPHEyrA-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Roborock is a brand well-versed in creating home robotic solutions that feel very futuristic. Earlier this year, <a href="https://www.tomsguide.com/home/home-appliances/roborock-launches-four-new-robot-vacuums-including-one-that-can-climb-stairs">it released four new robot vacuums, including one that can <em>climb stairs,</em></a> and we've recently tested the <a href="https://www.tomsguide.com/home/ive-been-testing-the-roborock-qv-35s-for-a-month-here-are-3-things-i-like-and-1-thing-i-dont">Roborock QV 35S</a>, a self-emptying robot vacuum with obstacle avoidance. But right now, it's focused on expanding its outdoor offering with a new robot lawnmower that has <em>a lot</em> to offer.</p><p>The RockNeo Q110H combines a host of smart features, including AI-powered mapping, precise edge-cutting, awareness of real-world conditions and more into one pretty impressive looking product. Not forgetting to mention, it also looks like it has a little face. And it's available to buy now for <a href="https://www.amazon.com/RockNeo-Q110H-Wire-Free-Full-Band-Navigation/dp/B0GRVYN12B?ref_=ast_sto_dp&th=1" target="_blank" rel="nofollow">$1299 at Amazon</a>. </p><div class="product"><a data-dimension112="52f5fc12-6c53-4273-80cb-ba51261ec3f9" data-action="Deal Block" data-label="The Roborock RockNeo Q110H robot lawnmower uses a collection of smart tech to create a low-stress setup for anyone with a small to medium-sized garden. If you're buying before June 16, there's also a 10% coupon to apply at Amazon." data-dimension48="The Roborock RockNeo Q110H robot lawnmower uses a collection of smart tech to create a low-stress setup for anyone with a small to medium-sized garden. If you're buying before June 16, there's also a 10% coupon to apply at Amazon." data-dimension25="$1299" href="https://www.amazon.com/roborock-RockMow-X115H-Robot-Mower/dp/B0GRVYN12B" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1696px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="d8cWH3cu2iPUDFr9HRMXEd" name="rockneo_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/d8cWH3cu2iPUDFr9HRMXEd.jpg" mos="" align="middle" fullscreen="" width="1696" height="1696" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Roborock RockNeo Q110H robot lawnmower uses a collection of smart tech to create a low-stress setup for anyone with a small to medium-sized garden. If you're buying before June 16, there's also a 10% coupon to apply at Amazon. <a class="view-deal button" href="https://www.amazon.com/roborock-RockMow-X115H-Robot-Mower/dp/B0GRVYN12B" target="_blank" rel="nofollow" data-dimension112="52f5fc12-6c53-4273-80cb-ba51261ec3f9" data-action="Deal Block" data-label="The Roborock RockNeo Q110H robot lawnmower uses a collection of smart tech to create a low-stress setup for anyone with a small to medium-sized garden. If you're buying before June 16, there's also a 10% coupon to apply at Amazon." data-dimension48="The Roborock RockNeo Q110H robot lawnmower uses a collection of smart tech to create a low-stress setup for anyone with a small to medium-sized garden. If you're buying before June 16, there's also a 10% coupon to apply at Amazon." data-dimension25="$1299">View Deal</a></p></div><h2 id="more-about-the-roborock-rockneo-q110h">More about the Roborock RockNeo Q110H</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="PuCxaCgDZ8AbV6M2y3MjmA" name="rockneo_3" alt="The Roborock RockNeo Q110H in the garden" src="https://cdn.mos.cms.futurecdn.net/PuCxaCgDZ8AbV6M2y3MjmA.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: Roborock)</span></figcaption></figure><p>The Roborock RockNeo Q110H joins a small line-up of robot lawnmowers for the company and, unsurprisingly, it's got a lot of the same technology built in. To get into the nitty-gritty of what's under the lid, we're talking "Sentisphere Environmental Perception" with "full-brand RTK and VSLAM navigation", "Stereo Vision Obstacle Avoidance", and an "optional PreciEdge cutting module." </p><p>To add a bit of color to those swanky feature titles, it basically means that the RockNeo Q110H assess real-world conditions to ensure it operates and understands the weather. </p><p>It'll also stick to dense tree cover and keep on course when navigating sharp corners and narrow passages.</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="7xk4giaQQYYWez5qhWEcxA" name="rockneo_5" alt="The Roborock RockNeo Q110H in the garden" src="https://cdn.mos.cms.futurecdn.net/7xk4giaQQYYWez5qhWEcxA.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: Roborock)</span></figcaption></figure><p>The RockNeo Q110H also utilizes obstacle avoidance technology to adjust its path in real-time. So, if there's toys or garden items lying around, it'll move around them without halting its progress. For humans and animals, it'll also turn away to stay safe (for both parties). And the optional PreciEdge module is an additional extra for helping to navigate near-perfect finishes across your garden edges.</p><p>And while other lawnmowers in Roborock's range, namely the RockMow X1, use AI-powered mapping already, there's an over-the-air upgrade coming soon that aims to further reduce any hard manual setup. </p><p>While all these features are a great addition to the RockNeo Q110H, I'm a particularly big fan of the Roborock App.</p><h2 id="a-companion-app-that-s-actually-good">A companion app that's actually good</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="QwpSTqFvhsUvSqJxz3VsrA" name="rockneo_1" alt="The Roborock RockNeo Q110H in the garden" src="https://cdn.mos.cms.futurecdn.net/QwpSTqFvhsUvSqJxz3VsrA.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: Roborock)</span></figcaption></figure><p>In the app, you can manage multiple zones of your garden with their own unique schedules, you can also monitor how your lawnmower is getting on, as well as getting a predicted finishing time. </p><p>There's even a Wildlife-Friendly Mode that will pause the lawnmower during set hours so that it won't bother nocturnal animals like hedgehogs who are navigating your garden. </p><p>This is where the AI-powered mapping upgrade comes in, arriving via the app, making setup and personlization even more impressive. And while it's sold separately, you can also add in a 4G and Anti-Theft Module that'll sound an alarm if the mower is lifted or moved outside a preset fence, all of which can be tracked in the app. </p><p>A smart lawnmower and a decent accompanying app to boot.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XrNGMX"></div>                            </div>                            <script src="https://kwizly.com/embed/XrNGMX.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/home/lawn-care/best-robot-lawnmowers-of-ces">Best robot lawnmowers of CES</a></li><li><a href="https://www.tomsguide.com/home/lawn-care/i-spent-a-season-with-the-ecovacs-goat-a3000-robot-lawn-mower-and-its-the-ultimate-time-saver?utm_source=facebook&utm_medium=social&utm_campaign=dhfacebook&utm_content=null&fbclid=IwY2xjawSUyhBleHRuA2FlbQIxMQBicmlkETF5WWV3SkR1ZW5qeWpLWmpjc3J0YwZhcHBfaWQQMjIyMDM5MTc4ODIwMDg5MgABHqDC3RLhxsJ5NklQyvkfkKiJzad06Pwgrci2ryhVCy2P5AqtahL1fjWVksKG_aem_5jhknQfq8Nf04E0Tpi3DUw">Is the Ecovacs Goat A3000 worth it? My honest review after four months of testing</a></li><li><a href="https://www.tomsguide.com/home/smart-home/i-just-tried-a-robot-lawnmower-for-the-first-time-heres-how-it-went">I just tried a robot lawnmower for the first time — here's how it went</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ How to sharpen a lawn mower blade in 5 simple steps (and save your grass) ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/lawn-care/how-to-sharpen-a-lawn-mower-blade-in-5-simple-steps-and-save-your-grass</link>
                                                                            <description>
                            <![CDATA[ Learn how to sharpen dull lawn mower blades to restore clean cuts and prevent lawn damage. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">vgFiEW75rtUiH5gbKy583Z</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/97WPqAwKqpyvVTSWNLwx3N-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 10 Jun 2026 08:30:00 +0000</pubDate>                                                                                                                                <updated>Fri, 26 Jun 2026 10:45:56 +0000</updated>
                                                                                                                                            <category><![CDATA[Lawn Care]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                <author><![CDATA[ kaycee.hill@futurenet.com (Kaycee Hill) ]]></author>                    <dc:creator><![CDATA[ Kaycee Hill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xHn6RmpEqg87cvtLwrBu9G.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/97WPqAwKqpyvVTSWNLwx3N-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A lawn mower cutting the grass]]></media:description>                                                            <media:text><![CDATA[A lawn mower cutting the grass]]></media:text>
                                <media:title type="plain"><![CDATA[A lawn mower cutting the grass]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/97WPqAwKqpyvVTSWNLwx3N-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Dull mower blades are the most overlooked cause of an <a href="https://www.tomsguide.com/home/outdoors/3-lawn-care-mistakes-silently-killing-your-grass-this-summer-and-how-to-avoid-them">unhealthy lawn</a>. Instead of cutting grass cleanly, dull blades tear and shred grass blades, leaving ragged edges that turn brown, invite disease, and stress roots. </p><p>Sharp mower blades can make all the difference between a pristine lawn and a damaged one. Sharpening your lawn mower blades takes about 30 minutes, requires no professional help, and saves you money on premature blade replacements. </p><p>Whether you're using a metal file or an angle grinder, this step-by-step guide covers exactly how to sharpen your mower blade safely, at the right angle, and correctly balanced, so your grass stays healthy all season long.</p><section class="howto-block">                    <h3>1) Prepare your mower for blade removal</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/kiAt9GaW4dFDBHE86DBtvD.jpg"                                        alt="A lawn mower cutting the grass"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/kiAt9GaW4dFDBHE86DBtvD.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Before you sharpen lawn mower blades, safety comes first. Disconnect power immediately. <strong>Electric mowers should be unplugged with the battery removed </strong>completely. </p><p><strong>For gas mowers, disconnect the spark plug wire </strong>to prevent accidental starting.<strong> </strong>To prevent gas spills, either drain the tank or place a piece of plastic wrap under the gas cap and screw it back on tightly to seal the vent hole.</p><p><strong>Carefully tip the mower onto its side</strong>, keeping the air filter and carburetor facing up to stop oil and gas from leaking into the engine. Before removing the blade, <strong>mark it with spray paint or a marker</strong> so you know which side faces downward.</p><p> Installing a mower blade upside-down means it simply won't cut, which is a frustrating mistake that's easy to avoid.</p></p>                </section><section class="howto-block">                    <h3>2) Remove and inspect the blade</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/FsqeoAKMXFa7LLdhNfmvpC.jpg"                                        alt="A lawn mower having it's blade removed with a wrench"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/FsqeoAKMXFa7LLdhNfmvpC.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p><strong>Use a wrench to loosen and remove the bolt or nut</strong>, keeping the fastener somewhere safe. With the blade off, <strong>take the opportunity to clean any grass buildup </strong>from inside the mower deck while you have clear access.</p><p><strong>Wipe the blade clean and inspect it carefully</strong>. Small nicks and dull edges are entirely fixable through sharpening. Deep cracks, large dents, or severe bends are a different story. Structural damage like that means the blade needs replacing, not sharpening.</p></p>                </section><section class="howto-block">                    <h3> 3) Sharpen the mower blade at the right angle</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/Z5X3uBECrVeLuy6oDjhgRM.jpg"                                        alt="An angle grinder being used to sharpen a lawn mower blade"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/Z5X3uBECrVeLuy6oDjhgRM.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>The correct angle to sharpen a mower blade is <strong>30 to 35 degrees</strong>,<strong> </strong>matching the manufacturer's original factory bevel. Getting this angle wrong is where most people's results fall short; a steeper angle will chop rather than cut, while a shallower angle will dull instantly.</p><p>Clamp the blade in a vise with one cutting edge facing up. A metal file held at 30 degrees, pushed away from your body in smooth, consistent strokes, is the most controlled method and ideal if you haven't done this before. Around 50 strokes per edge is usually enough<strong>. The goal is "butter knife sharp." </strong></p><p>An angle grinder gets the job done faster and works just as well in experienced hands. The main thing to watch is heat. Use light pressure and take frequent breaks to let the metal air-cool. </p><p><strong>Never dip a hot blade into cold water</strong>, as this can quench-harden the steel unevenly, making it brittle and prone to shattering. Whichever method you use, <strong>sharpen one edge fully before flipping the blade and repeating</strong> on the other side. Expect around 15 minutes of sharpening time either way.</p></p>                </section><section class="howto-block">                    <h3>4) Check and correct blade balance</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/az8QYgwiUbb7oGbDTaNQ9k.jpg"                                        alt="A damaged lawn mower blade"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/az8QYgwiUbb7oGbDTaNQ9k.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Skipping the balance check is one of the most common mower blade mistakes. An unbalanced blade vibrates excessively, and over time that vibration wears out the engine and spindle bearing. </p><p><strong>Rest the blade horizontally on a nail or screwdriver through the center hole</strong>. If one side dips, that side is heavier and needs more metal removed. <strong>Go back to sharpening and take a little more off the heavy side </strong>until the blade sits level. </p><p>A dedicated blade balancer tool, inexpensive and available at most hardware stores, gives a more precise result. </p></p>                </section><section class="howto-block">                    <h3>5) Reinstall and test your sharpened blade</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/Hrn5WkBxseADQ8gvknqdeC.jpg"                                        alt="A lawn mower cutting the grass"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/Hrn5WkBxseADQ8gvknqdeC.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Using your earlier markings, <strong>verify the blade is facing the correct direction before reinstalling</strong>. Tighten the bolt or nut securely to the manufacturer's torque specifications. </p><p><strong>Remove the plastic wrap from the gas cap </strong>if you used it, <strong>reconnect the spark plug wire, and do a test cut</strong> on a small patch of grass.</p><p>A properly sharpened blade cuts cleanly with no tearing or ragged edges. If the quality is still poor after sharpening, remove the blade and sharpen more aggressively, or consider whether replacement is the better call.</p></p>                </section><h2 id="how-often-should-i-sharpen-my-lawn-mower-blade">How often should I sharpen my lawn mower blade? </h2><p>Sharpen your lawn mower blades after every 25 hours of mowing, which is roughly twice per season for regular users. </p><p>Beyond the schedule, torn or ragged grass edges after cutting, a lawn that yellows a day or two after mowing, or visible nicks on the blade are all signs it needs attention sooner. Hitting a rock, stick, or hard object while mowing warrants an immediate inspection too.</p><p>Always sharpen before storing your mower for winter. It prevents corrosion and means the mower is ready to go the moment spring arrives.</p><p>Not every dull blade is worth sharpening. Cracks or fractures anywhere on the blade, a severe bend that sharpening won't correct, or metal worn so thin from repeated sharpening that it won't hold an edge — these are all signs replacement is the right move. <br><br>Once you've sharpened your mower blades and mown your lawn, it’s worth leaving the <a href="https://www.tomsguide.com/home/gardeners-urged-not-to-make-this-mistake-with-their-grass-clippings-heres-why-you-shouldnt-throw-them-away">grass clippings</a> on your lawn. They’ll act as a natural mulch and fertilizer.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eG0N2W"></div>                            </div>                            <script src="https://kwizly.com/embed/eG0N2W.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/home/lawn-care/11-secrets-to-a-lawn-thats-greener-than-your-neighbors">11 secrets to a lawn that’s greener than your neighbor's</a></li><li><a href="https://www.tomsguide.com/home/gardening/ant-hills-multiplying-how-to-destroy-the-colony-before-it-takes-over-your-yard">Ant hills multiplying? How to destroy the colony before it takes over your yard</a></li><li><a href="https://www.tomsguide.com/home/7-ways-to-banish-weeds-from-your-lawn-that-wont-harm-the-grass">7 tips to banish weeds from your lawn that won't harm the grass</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Want to grill like a pro? A Michelin star chef reveals his secret to perfect grilling — and I've been doing it all wrong ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/outdoors/want-to-grill-like-a-pro-a-michelin-star-chef-reveals-his-secret-to-perfect-grilling-and-ive-been-doing-it-all-wrong</link>
                                                                            <description>
                            <![CDATA[ Stop grilling all wrong — I asked a professional chef for his top tip that will transform your BBQ results. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">RWDbuuUzhXqKgfdDrvbYGg</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/TPRh6SFa24D7gqPhRjdZ6c-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Wed, 10 Jun 2026 05:45:00 +0000</pubDate>                                                                                                                                <updated>Wed, 10 Jun 2026 12:20:59 +0000</updated>
                                                                                                                                            <category><![CDATA[Outdoors]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                                                                                    <dc:creator><![CDATA[ Cynthia Lawrence ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/CCuSgQpd5NyZ46CgoF9cva.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/png" url="https://cdn.mos.cms.futurecdn.net/TPRh6SFa24D7gqPhRjdZ6c-1280-80.png">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Grilling outside]]></media:description>                                                            <media:text><![CDATA[Grilling outside]]></media:text>
                                <media:title type="plain"><![CDATA[Grilling outside]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/TPRh6SFa24D7gqPhRjdZ6c-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Now that the weather is warmer, I’m looking forward to getting my charcoal <a href="https://www.tomsguide.com/best-picks/best-grills">grill </a>out of hibernation and preparing for our summer backyard gatherings. But despite my love for those delicious smoky flavors, I must admit that my grilling technique is always hit-and-miss.</p><p>That’s why I decided to ask a professional chef what the secret is to perfecting your grilling game. As it turns out, I've been approaching it wrong all this time — but not anymore. </p><p>So if you want to improve your succulent steaks or simply wish to impress your guests, here’s the top grilling tip we all must do for that perfect taste. <a href="https://www.tomsguide.com/best-picks/best-grills"> </a></p><div class="product"><a data-dimension112="1fe3c77d-5222-489d-a7e7-46c8e533707f" data-action="Deal Block" data-label="This charcoal grill's grate is big enough for a whole mess of hamburgers. It’s easy to adjust the heat by opening and closing the vents in the bottom and in the lid, making it remarkably versatile. You can go beyond burgers and brats to make perfectly seared steaks and golden-brown chicken. Extras include a thermometer in the lid and hooks for hanging up your tools." data-dimension48="This charcoal grill's grate is big enough for a whole mess of hamburgers. It’s easy to adjust the heat by opening and closing the vents in the bottom and in the lid, making it remarkably versatile. You can go beyond burgers and brats to make perfectly seared steaks and golden-brown chicken. Extras include a thermometer in the lid and hooks for hanging up your tools." data-dimension25="$347" href="https://www.amazon.com/dp/B07KT9FY31" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:890px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="oprQAcc2dBXKGJ7ZwAnvJA" name="weber_kettle_grill2_-_jpg.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/oprQAcc2dBXKGJ7ZwAnvJA.jpg" mos="" align="middle" fullscreen="" width="890" height="890" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This charcoal grill's grate is big enough for a whole mess of hamburgers. It’s easy to adjust the heat by opening and closing the vents in the bottom and in the lid, making it remarkably versatile. You can go beyond burgers and brats to make perfectly seared steaks and golden-brown chicken. Extras include a thermometer in the lid and hooks for hanging up your tools.<a class="view-deal button" href="https://www.amazon.com/dp/B07KT9FY31" target="_blank" rel="nofollow" data-dimension112="1fe3c77d-5222-489d-a7e7-46c8e533707f" data-action="Deal Block" data-label="This charcoal grill's grate is big enough for a whole mess of hamburgers. It’s easy to adjust the heat by opening and closing the vents in the bottom and in the lid, making it remarkably versatile. You can go beyond burgers and brats to make perfectly seared steaks and golden-brown chicken. Extras include a thermometer in the lid and hooks for hanging up your tools." data-dimension48="This charcoal grill's grate is big enough for a whole mess of hamburgers. It’s easy to adjust the heat by opening and closing the vents in the bottom and in the lid, making it remarkably versatile. You can go beyond burgers and brats to make perfectly seared steaks and golden-brown chicken. Extras include a thermometer in the lid and hooks for hanging up your tools." data-dimension25="$347">View Deal</a></p></div><h3 class="article-body__section" id="section-learn-to-cook-with-the-fire"><span>Learn to cook with the fire</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:5751px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XstUdQgiKctGTU5XbKcWXJ" name="Grilling over flame - crop.jpg" alt="Grilling over a flame" src="https://cdn.mos.cms.futurecdn.net/XstUdQgiKctGTU5XbKcWXJ.jpg" mos="" align="middle" fullscreen="" width="5751" height="3235" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Grilling over a flame </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>Mastering the art of cooking properly with the fire is a total game-changer. My usual routine involved just spreading everything across the grates and waiting patiently for one side to finish before flipping over. But as it turns out, the secret lies in staying active and constantly shifting your ingredients across the heat.</p><p>“For me, barbecue cooking is one of the purest and most enjoyable ways to cook. It’s instinctive, hands-on and social - proper primal cooking over fire!” states Tommy Banks, British Chef and brand ambassador for<a href="https://kasaigrills.com/" target="_blank"> Kasai Grills</a>.</p><div><blockquote><p>Cooking over fire is very instinctive. You have to stay involved with it. A great barbecue isn’t about putting something down and walking away.</p><p>Tommy Banks, British Chef</p></blockquote></div><p>“Cooking over fire is very instinctive," Banks says. "You have to stay involved with it. A great barbecue isn’t about putting something down and walking away. It’s about moving food around constantly, understanding where the hotspots and cooler spots are, adjusting heights and controlling caramelization and smoke levels. That movement is what gives you beautifully cooked food instead of burnt food!”</p><p>Of course, always proceed with caution whenever cooking over a flame, and avoid doing this near children or pets running around. </p><h3 class="article-body__section" id="section-fire-it-up-early"><span>Fire it up early</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:6038px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="7Lv7KTYfFsvSithENPK3Di" name="Grilling steaks - crop.jpg" alt="Meat steaks and food on a grill" src="https://cdn.mos.cms.futurecdn.net/7Lv7KTYfFsvSithENPK3Di.jpg" mos="" align="middle" fullscreen="" width="6038" height="3396" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Meat steaks and food on a grill </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>Another common mistake people often make is not firing up the grill early enough. This is especially the case for lump wood charcoal, where you’ll need to allow adequate time for it to heat up. Again, this will make all the difference to your tasty results. </p><p>“People get impatient and start cooking while there are still flames licking everywhere,” explains Banks. “You need to let the flames die down and cook over the glowing coals instead.”</p><p>So what’s the best fuel for grilling like a pro and for the best results? “A premium lump wood charcoal is probably the best and easiest option for most people cooking at home," Banks says. "If you really want to level things up, binchotan charcoal is incredible.</p><p>"That’s what we use in the restaurants because it burns far hotter and much longer than standard charcoal. In a professional kitchen, the last thing you want is your barbecue cooling down halfway through service.”</p><p>Armed with these professional tips, I am keen to see if these will actually level up my outdoor cooking and truly impress my guests at our next backyard gathering. Watch this space.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-evLlmO"></div>                            </div>                            <script src="https://kwizly.com/embed/evLlmO.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/best-picks/best-meat-thermometers">The best meat thermometers we've tested</a></li><li><a href="https://www.tomsguide.com/home/home-appliances/3-things-to-consider-before-buying-a-grill">3 things to consider before buying a grill</a></li><li><a href="https://www.tomsguide.com/home/outdoors/how-to-stay-safe-this-bbq-season-9-grilling-safety-checks-youre-probably-forgetting">9 grilling safety checks you're probably forgetting</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Why gardeners are sprinkling grill ash on their plants (it's not as surprising as it sounds) ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/why-gardeners-are-sprinkling-grill-ash-on-their-plants-its-not-as-surprising-as-it-sounds</link>
                                                                            <description>
                            <![CDATA[ Forget fertilizer — this unexpected grill waste could give these popular plants a healthy boost and help them thrive all season. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">tZSZRaAEnYanEDvtugkbfC</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/kPpiBTiUqAq9DUwVjyBvvV-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 08 Jun 2026 12:27:13 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                                    <dc:creator><![CDATA[ Cynthia Lawrence ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/CCuSgQpd5NyZ46CgoF9cva.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/kPpiBTiUqAq9DUwVjyBvvV-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Grill ash]]></media:description>                                                            <media:text><![CDATA[Grill ash]]></media:text>
                                <media:title type="plain"><![CDATA[Grill ash]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/kPpiBTiUqAq9DUwVjyBvvV-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Now that warmer weather is finally here, many households have already put their<a href="https://www.tomsguide.com/best-picks/best-grills"> best grill</a> to good use, or are getting ready to host those backyard cookouts (World Cup watch parties, anyone?). And if you prefer cooking with wood charcoal, don’t throw out your leftover ash after your feasts. </p><p>Surprisingly, wood charcoal ash is valuable to certain plants and will help to give them a healthy boost. And gardeners are sprinkling their ash leftovers as a great soil enhancer — effectively neutralizing soil pH while providing a powerhouse of essential nutrients, including potassium, calcium, phosphorus and magnesium — that will help your garden thrive. </p><p>From herbs and juicy tomatoes to beautiful blooms such as roses, who knew that our smoky remnants would be liquid gold for our plants? Before you sprinkle ash into your soil, however, it’s recommended to do a test to check pH. This simple step identifies the current alkalinity levels and ensures you apply just the right amount to your plants.</p><p>In addition, avoid using wood ash around acid-loving varieties and never use it on delicate seedlings, in case it causes more harm than good! For best results, stick to plants that flourish in more alkaline environments.</p><p>So before you ditch your grill waste, put it to good use in the garden instead. Here are just a few of our favorite plants that will love your leftover charcoal ash. </p><h3 class="article-body__section" id="section-succulent-strawberries"><span>Succulent strawberries </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:3494px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="NraeewVL3ZiGsJJweJVGCn" name="Strawberry pots - crop.jpg" alt="Three pots of strawberry plants on table" src="https://cdn.mos.cms.futurecdn.net/NraeewVL3ZiGsJJweJVGCn.jpg" mos="" align="middle" fullscreen="" width="3494" height="1965" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Three pots of strawberry plants on table </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>There’s nothing better than <a href="https://www.tomsguide.com/home/how-to-grow-endless-strawberries-for-free-using-grocery-store-scraps-its-a-smart-savings-hack">fresh, juicy strawberries</a>, and if you want a bountiful harvest this summer, sprinkling grill ash can give them that much-needed boost. </p><p>Strawberries thrive in excellent drainage conditions, so adding wood ash to the compost (before planting) can significantly help to loosen soil and improve drainage — especially when dealing with heavy clay soil. Generally, strawberries prefer a soil pH between 5.5 and 7.0, which ranges from fairly acidic to mildly alkaline. </p><h3 class="article-body__section" id="section-tastier-tomatoes"><span>Tastier tomatoes</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:4000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="PSUw2CLaAQeZSVeUE3ALpY" name="Growing tomatoes in raised garden bed with marigolds" alt="Growing tomatoes in raised garden bed with marigolds" src="https://cdn.mos.cms.futurecdn.net/PSUw2CLaAQeZSVeUE3ALpY.jpg" mos="" align="middle" fullscreen="" width="4000" height="2250" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Growing tomatoes in raised garden bed with marigolds  </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>Another popular plant we all love growing here at <a href="https://www.tomsguide.com/home/gardening/toms-guide-to-tomatoes">Tom’s Guide is tomatoes</a>. But did you know that wood ash can make a great fertilizer before planting tomato seedlings in a bed?</p><p>Similarly, tomatoes require loose, well-draining soil to grow healthy and in abundance. Adding ash around the plant bed, this can improve soil quality and add essential nutrients such as potassium that can help to prevent diseases such as blossom end rot. Again, before you sprinkle ash, always check the results of your soil tests since tomatoes are a delicate plant. </p><p>For more top tips, find out<a href="https://www.tomsguide.com/home/gardening/when-to-fertilize-your-tomatoes-and-how-to-get-a-bountiful-harvest"> when to fertilize tomatoes: the 3-step schedule for a bountiful harvest.</a></p><h3 class="article-body__section" id="section-rosy-roses"><span>Rosy roses</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:5246px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XPSNidUYYCn5YzgGyiVCEf" name="Pink roses - crop.jpg" alt="Pink roses" src="https://cdn.mos.cms.futurecdn.net/XPSNidUYYCn5YzgGyiVCEf.jpg" mos="" align="middle" fullscreen="" width="5246" height="2951" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Pink roses </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>If you love roses or simply want to <a href="https://www.tomsguide.com/home/gardening/7-expert-tips-to-grow-the-perfect-roses-this-summer">grow the perfect roses this summer</a>, there are plenty of things to consider. More importantly, and depending on the variety, you’ll need the right soil pH level for them to thrive and stay healthy all season. </p><p>Typically, most rose varieties grow as perennials in Zones 5 to 11 and require a soil pH between 6.0 (slightly acidic) and 8.0 (alkaline). Depending on your soil test readings, you can repurpose your grill ash to amend the soil pH for your beautiful blooms — and get rid of nuisance pests too. This will help to ensure you have a healthy and vibrant rose garden to enjoy at all times. </p><h3 class="article-body__section" id="section-don-t-use-just-any-grill-ash"><span>Don't use just any grill ash!</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:4692px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="N4bRpzCf5X2iWQHP2SATpZ" name="Grilling outdoors main - crop.jpg" alt="Man grilling food on barbecue outside" src="https://cdn.mos.cms.futurecdn.net/N4bRpzCf5X2iWQHP2SATpZ.jpg" mos="" align="middle" fullscreen="" width="4692" height="2639" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Man grilling food on barbecue outside </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>While wood ash can certainly benefit your garden, it’s critical to remember that not every smoky remnant is good for the soil. To protect your garden's health, you should strictly avoid using leftovers from fatty briskets or any charcoal that required chemical lighter fluids, as these harmful additives can easily ruin your soil quality.</p><div class="product"><a data-dimension112="1fe3c77d-5222-489d-a7e7-46c8e533707f" data-action="Deal Block" data-label="This charcoal grill's grate is big enough for a whole mess of hamburgers. However, because it’s easy to adjust the heat by opening and closing the vents in the bottom and in the lid, this grill is remarkably versatile and can go beyond burgers and brats to make perfectly seared steaks and golden-brown chicken. Extras include a thermometer in the lid and hooks for hanging up your tools." data-dimension48="This charcoal grill's grate is big enough for a whole mess of hamburgers. However, because it’s easy to adjust the heat by opening and closing the vents in the bottom and in the lid, this grill is remarkably versatile and can go beyond burgers and brats to make perfectly seared steaks and golden-brown chicken. Extras include a thermometer in the lid and hooks for hanging up your tools." data-dimension25="$347" href="https://www.amazon.com/dp/B07KT9FY31" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:890px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="oprQAcc2dBXKGJ7ZwAnvJA" name="weber_kettle_grill2_-_jpg.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/oprQAcc2dBXKGJ7ZwAnvJA.jpg" mos="" align="middle" fullscreen="" width="890" height="890" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This charcoal grill's grate is big enough for a whole mess of hamburgers. However, because it’s easy to adjust the heat by opening and closing the vents in the bottom and in the lid, this grill is remarkably versatile and can go beyond burgers and brats to make perfectly seared steaks and golden-brown chicken. Extras include a thermometer in the lid and hooks for hanging up your tools.<a class="view-deal button" href="https://www.amazon.com/dp/B07KT9FY31" target="_blank" rel="nofollow" data-dimension112="1fe3c77d-5222-489d-a7e7-46c8e533707f" data-action="Deal Block" data-label="This charcoal grill's grate is big enough for a whole mess of hamburgers. However, because it’s easy to adjust the heat by opening and closing the vents in the bottom and in the lid, this grill is remarkably versatile and can go beyond burgers and brats to make perfectly seared steaks and golden-brown chicken. Extras include a thermometer in the lid and hooks for hanging up your tools." data-dimension48="This charcoal grill's grate is big enough for a whole mess of hamburgers. However, because it’s easy to adjust the heat by opening and closing the vents in the bottom and in the lid, this grill is remarkably versatile and can go beyond burgers and brats to make perfectly seared steaks and golden-brown chicken. Extras include a thermometer in the lid and hooks for hanging up your tools." data-dimension25="$347">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-evLlmO"></div>                            </div>                            <script src="https://kwizly.com/embed/evLlmO.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/how-to/7-composting-tips-everyone-needs-to-know">7 composting tips everyone needs to know</a></li><li><a href="https://www.tomsguide.com/home/gardening/your-peace-lily-wont-survive-winter-if-you-dont-change-these-5-things">Your peace lily won't survive winter if you don't change these 5 things</a></li><li><a href="https://www.tomsguide.com/home/dont-let-winter-de-icing-ruin-your-lawn-5-grass-safe-ways-to-clear-ice">Stop killing your grass: 5 safe de-icing methods you need to know</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Rosemary plant turning brown? Start with the "scratch test" to save it ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/rosemary-plant-turning-brown-start-with-the-scratch-test-to-save-it</link>
                                                                            <description>
                            <![CDATA[ The simple way to tell why your rosemary leaves are browning — and how to save it. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">dKeF5Ew3KXpcMruY9LhDbd</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/TsWtjj7g8N2m5eGiTY7uMN-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 08 Jun 2026 08:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                                    <dc:creator><![CDATA[ Alice Carroll ]]></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/TsWtjj7g8N2m5eGiTY7uMN-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Browning rosemary plant]]></media:description>                                                            <media:text><![CDATA[Browning rosemary plant]]></media:text>
                                <media:title type="plain"><![CDATA[Browning rosemary plant]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/TsWtjj7g8N2m5eGiTY7uMN-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Rosemary growing wild might make you think it's unusually hardy, but like any plant, the wrong conditions can cause harm. Browning leaves are usually a sign that something is off — whether that's too much water, too little sun, or a sudden change in temperature. </p><p>Most rosemary plants can be brought back to full health with a little attention and the right fix. Before reaching for a new plant, it's worth taking a closer look at what your rosemary is trying to tell you. </p><p>Read on to find out what different signs indicate, and what to do next to restore your plant to its full, green vitality.</p><h2 id="is-your-rosemary-dead-or-just-stressed">Is your rosemary dead or just stressed?</h2><p>The best way to find out is to use the scratch test. <strong>Use a fingernail to scratch a small portion of the main stem</strong>. If you see green, moist tissue, the plant is still alive, but if the stem is brittle and brown, that branch is dead. </p><p>If you can only find brown, brittle stems, the plant is beyond saving — it’s time to start again with a new one. If you see any green, though, your rosemary may still thrive with a little extra care.</p><h3 class="article-body__section" id="section-the-main-causes-of-browning-and-how-to-fix-them"><span>The main causes of browning (and how to fix them)</span></h3><section class="howto-block">                    <h3>1. Root rot </h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/tfoznGcdxbcmvFTEmE6Q2U.jpg"                                        alt="Watering rosemary plant"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/tfoznGcdxbcmvFTEmE6Q2U.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>If your rosemary has been overwatered or if it is in compacted soil or pots without drainage holes, the plant can develop root rot. <br><br>Root rot occurs when too much water overwhelms the roots and encourages fungal diseases. <strong>Check the soil around the plant’s roots for moisture and porosity </strong>— that is, how densely the soil is packed and how easily it can drain. </p><p>If your plant is outdoors and you’ve recently had a rainy spell, the rosemary may just need some time to dry out. </p></p>                </section><section class="howto-block">                    <h3>2. Underwatering</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/yb7jjnjZoRXrAQq7qfZv9n.jpg"                                        alt="Brown and brittle rosemary plant"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/yb7jjnjZoRXrAQq7qfZv9n.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>While rosemary generally does well in dry soil, too much dryness will render the leaves crispy and brittle. If your plant feels very dry and the leaves have lost their flexibility, <strong>give it a thorough watering</strong> — enough for water to drain freely from the bottom of the pot. </p><p>Going forward, potted rosemary generally needs watering once a week, while outdoor plants only need additional watering during long dry spells. </p><p>Dry air from indoor heating can also dry out the leaves. If this is the case,<strong> try misting the plant occasionally or placing a small tray of water nearby </strong>to add some moisture to the air.</p></p>                </section><section class="howto-block">                    <h3>3. Temperature shock</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/K29BsmBrak6Zmod6X7DjbV.jpg"                                        alt="Rosemary in a pot next to a windowsill"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/K29BsmBrak6Zmod6X7DjbV.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Sudden changes in temperature can shock rosemary and cause leaves to drop. In hot weather, intense sun and heat are often the culprit — if potted, <strong>move the plant to a spot with partial shade </strong>during the hottest part of the day, and make sure it isn't sitting on a surface that absorbs and radiates heat, like concrete or stone. </p><p>If the plant has been moved recently (from outdoors to indoors, for example), <strong>give it time to adjust to its new environment</strong>. </p><p>For indoor plants,<strong> keep them away from windows prone to drafts, heating vents, and air conditioners</strong>, all of which can cause sudden temperature fluctuations that stress the plant and cause brown leaves. </p></p>                </section><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-X7DKde"></div>                            </div>                            <script src="https://kwizly.com/embed/X7DKde.js" async></script><figure class="van-image-figure pull-right inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:676px;"><p class="vanilla-image-block" style="padding-top:31.51%;"><img id="ZCwuUFRE3YeboHR4zyEWmb" name="r3t8zZ5ve4GewFTeoCM3R6" alt="Google" src="https://cdn.mos.cms.futurecdn.net/ZCwuUFRE3YeboHR4zyEWmb.jpg" mos="" align="right" fullscreen="" width="676" height="213" attribution="" endorsement="" class="pull-rightinline"></p></div></div><figcaption itemprop="caption description" class="pull-right inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></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/gardening/why-are-my-tomato-plant-leaves-turning-yellow-5-steps-to-save-your-harvest">Why are my tomato plant leaves turning yellow? 5 steps to save your harvest</a></li><li><a href="https://www.tomsguide.com/home/gardening/forget-buying-new-seeds-these-9-gorgeous-flowers-come-back-for-free-every-year">Forget buying new seeds: these 9 gorgeous flowers come back for free every year</a></li><li><a href="https://www.tomsguide.com/home/gardening/5-reasons-your-lemon-tree-leaves-are-turning-yellow-and-how-to-reverse-the-damage">Yellow lemon tree leaves? Here’s how to get them lush and green again</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I just spent $25 on a garden must-have that waters my plants naturally —  saving me the effort ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/i-just-spent-usd25-on-a-garden-must-have-that-waters-my-plants-naturally-saving-me-the-effort</link>
                                                                            <description>
                            <![CDATA[ This $25 garden essential waters my plants naturally — without me having to lift a finger. So what exactly are Ollas? ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">oCkRwhRgwUDP9wpatXX9SP</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/xfrHYffjtTUNt7qtVyerrf-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 07 Jun 2026 04:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                                    <dc:creator><![CDATA[ Cynthia Lawrence ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/CCuSgQpd5NyZ46CgoF9cva.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/xfrHYffjtTUNt7qtVyerrf-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Ollas irrigation pot for flower bed]]></media:description>                                                            <media:text><![CDATA[Ollas irrigation pot for flower bed]]></media:text>
                                <media:title type="plain"><![CDATA[Ollas irrigation pot for flower bed]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/xfrHYffjtTUNt7qtVyerrf-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>With warmer temperatures here, our plants and gardens will also be feeling the heat this summer. And whether you’re a plant parent or growing veggies, it’s vital to keep up your watering schedule, or else your plants will be literally parched and end up <a href="https://www.tomsguide.com/news/how-to-save-a-dying-plant">dying</a>.</p><p>Admittedly, I’m guilty of neglecting my watering schedule, mostly because I simply lack the time (or the energy)  after a long day. That’s when I discovered this clever gardening essential that will water my flower beds naturally — without me having to lift a finger. </p><p>In fact, this method is a centuries-old<a href="https://www.tomsguide.com/home/gardener-reveals-centuries-old-hack-for-keeping-plants-hydrated-in-the-summer"> water irrigation system</a> that involves porous clay or terracotta pots. Best of all, it’s simple to use and won’t break the bank. So what are Ollas irrigation pots, and why are they my must-have tool?</p><div class="product"><a data-dimension112="05c4b791-db6c-46cb-861d-eb1bc1f98e65" data-action="Deal Block" data-label="Made from terracotta, these are 18 inches in diameter to support two large plants. These also hold 700 ml of water, which is enough to get your plants through warmer weather, and it has a weather-proof rubber stopper to prevent water evaporating." data-dimension48="Made from terracotta, these are 18 inches in diameter to support two large plants. These also hold 700 ml of water, which is enough to get your plants through warmer weather, and it has a weather-proof rubber stopper to prevent water evaporating." data-dimension25="$29" href="https://www.amazon.com/Back-Roots-Self-Watering-Terracotta-Precision/dp/B0D8PT296S" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1454px;"><p class="vanilla-image-block" style="padding-top:103.16%;"><img id="XAnjD4K6KWfyPomwV4u7hh" name="Back to the Roots Self-Watering Terracotta Olla Pot (Pack of 3)" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/XAnjD4K6KWfyPomwV4u7hh.jpg" mos="" align="middle" fullscreen="" width="1454" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Made from terracotta, these are 18 inches in diameter to support two large plants. These also hold 700 ml of water, which is enough to get your plants through warmer weather, and it has a weather-proof rubber stopper to prevent water evaporating.<a class="view-deal button" href="https://www.amazon.com/Back-Roots-Self-Watering-Terracotta-Precision/dp/B0D8PT296S" target="_blank" rel="nofollow" data-dimension112="05c4b791-db6c-46cb-861d-eb1bc1f98e65" data-action="Deal Block" data-label="Made from terracotta, these are 18 inches in diameter to support two large plants. These also hold 700 ml of water, which is enough to get your plants through warmer weather, and it has a weather-proof rubber stopper to prevent water evaporating." data-dimension48="Made from terracotta, these are 18 inches in diameter to support two large plants. These also hold 700 ml of water, which is enough to get your plants through warmer weather, and it has a weather-proof rubber stopper to prevent water evaporating." data-dimension25="$29">View Deal</a></p></div><h3 class="article-body__section" id="section-what-are-ollas-irrigation-pots-and-how-do-they-work"><span>What are Ollas irrigation pots and how do they work?</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:5778px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="aLFHhDrgwHbMYX6T9cjxRB" name="shutterstock_2759845467" alt="Ollas irrigation pots in raised bed of soil" src="https://cdn.mos.cms.futurecdn.net/aLFHhDrgwHbMYX6T9cjxRB.jpg" mos="" align="middle" fullscreen="" width="5778" height="3251" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Ollas irrigation pots in raised bed of soil </span><span class="credit" itemprop="copyrightHolder">(Image credit: Shutterstock)</span></figcaption></figure><p>Ollas (pronounced ohh-yah) are porous earthenware, clay, or terracotta pots. At first glance, it looks like an ordinary clay pot with a bulb-like appearance. But the real game-changer is its ability to slowly release water into the soil and the roots of plants when it needs it.</p><p>In fact, this isn’t a clever new viral ‘hack’ — the Ollas water irrigation system was first used 4,000 years ago in China. You simply bury the terracotta ollas pot underground until its neck, fill it with water, and it will leach out water only when the soil gets dry. This is because the water is drawn out by osmosis, thanks to its porous material. What’s more, if the ground is wet, the water will remain in the olla, which means there is no risk of overwatering the soil. Impressive!</p><p>Not only does Ollas do the hard work for you, but encourages deeper root growth.  For instance, if you just water the surface, this encourages roots upwards. However, deeper roots allow your plants or veggies to tap into moisture and nutrients deeper in the soil, and they can also stay cooler. Additionally, you can add liquid fertilizer to the Ollas if you want to give your roots a nutritional boost. </p><h3 class="article-body__section" id="section-how-i-used-ollas-in-my-yard"><span>How I used Ollas in my yard</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:2715px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="gFnuaz5xvkQasJyoqYoHwM" name="Ollas irrigation pot sitting in hole in soil" alt="Ollas irrigation pot sitting in hole in soil" src="https://cdn.mos.cms.futurecdn.net/gFnuaz5xvkQasJyoqYoHwM.jpg" mos="" align="middle" fullscreen="" width="2715" height="1527" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Compost tumbler </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Ever since we started a DIY greenhouse and raised bed area, the real challenge is that the soil can often get dry quickly — especially with the temperatures rising.  </p><p>Besides setting an alarm reminder on my phone, the ollas offer a much better way to water the new seeds or plants regularly. Best of all, setting it up is a breeze and only takes a few minutes of your time. </p><p>First, I dug a small hole in the soil to bury the olla, leaving 1-2 inches above the soil surface (with the neck out) to keep dirt and soil from getting inside. </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:2933px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="2VkNdcKc22PawWxDryLE2a" name="Ollas irrigation pot buried in soil" alt="Ollas irrigation pot buried in soil" src="https://cdn.mos.cms.futurecdn.net/2VkNdcKc22PawWxDryLE2a.jpg" mos="" align="middle" fullscreen="" width="2933" height="1650" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Ollas irrigation pot buried in soil </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Next, I packed the surrounding soil closely around the Ollas to prevent air pockets, as you want to ensure that the water can easily move into the soil and keep roots growing healthy.</p><p>I used a watering can to pour water in through the top before putting the lid on — then you simply leave it to do its work. The water will slowly leach out to provide essential hydration to the surrounding soil, keeping your thirsty plants quenched.</p><p>You'll need to refill your Ollas once or twice a week, depending on the water level, as you don’t want it to dry out. Also, if you have more than one Ollas, be sure to space them correctly — the general rule of thumb is every 2 – 3 feet in the yard for best irrigation. You can also buy various sizes to suit your outdoor space and even use in your pots or containers. Just remember where you've buried them all!</p><p>I still have yet to see how well my vegetable seeds will grow; however, this affordable water irrigation is a must-have for any gardener. Plus, it comes in handy as a self-watering pot while you’re on summer vacation too — which is a bonus.   </p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li>How<a href="https://www.tomsguide.com/home/9-expert-tips-on-how-to-save-water-in-your-yard-this-summer"> to save water in your yard this summer</a></li><li><a href="https://www.tomsguide.com/home/5-ways-to-keep-your-lawn-healthy-and-green-without-wasting-water">5 ways to keep your lawn healthy and green — without wasting water</a></li><li><a href="https://www.tomsguide.com/home/this-nasa-approved-plant-hack-cools-your-home-for-free-heres-how">This NASA-approved plant hack cools your home for free</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I discovered this $10 IKEA tool  — and it will help you keep cool without breaking the budget ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/i-found-this-usd10-ikea-tool-in-the-smart-home-section-thatll-help-you-keep-cool-without-breaking-the-budget</link>
                                                                            <description>
                            <![CDATA[ IKEA's smart home range has a hidden gem that'll help you battle heatwaves this summer — and it's super affordable. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">5igyjUeaFwxdi3J6DVo9kn</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/XeZw5rCFYWmkEjGEWCTj9N-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 05 Jun 2026 08:01:00 +0000</pubDate>                                                                                                                                <updated>Fri, 05 Jun 2026 20:17:48 +0000</updated>
                                                                                                                                            <category><![CDATA[Home]]></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/XeZw5rCFYWmkEjGEWCTj9N-1280-80.jpg">
                                                            <media:credit><![CDATA[IKEA]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The IKEA TIMMERFLOTTE device next to a bathroom sink]]></media:description>                                                            <media:text><![CDATA[The IKEA TIMMERFLOTTE device next to a bathroom sink]]></media:text>
                                <media:title type="plain"><![CDATA[The IKEA TIMMERFLOTTE device next to a bathroom sink]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/XeZw5rCFYWmkEjGEWCTj9N-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>When I first looked into <a href="https://www.tomsguide.com/home/a-familiar-and-easy-connectivity-experience-without-financial-burden-ikea-and-samsung-want-to-streamline-your-smart-home-heres-how">IKEA's affordable smart home devices</a>, I realized I could now give my home a futuristic makeover without having to part with a load of cash. Smart home gadgets have always felt a little out of my price range or a little overwhelming, so inviting a few of these into my home to try out on a budget has been a fun endeavor.</p><p>Then, when the hot weather hit, I realized one of these devices was going to be a game changer for the rest of summer – the <a href="https://www.ikea.com/us/en/p/timmerflotte-temperature-humidity-sensor-smart-50618957/" target="_blank">TIMMERFLOTTE tempereature/humidity sensor</a>.</p><p>Why? Well, it's actually pretty simple. With a constant digital readout of the temperature or humidity, it helps me keep a check on what's happening inside my home. Not only that, but you can hook it up to anything else in the <a href="https://www.ikea.com/us/en/product-guides/ikea-home-smart-system/ikea-matter-compatible-system-pub7ab815e0/" target="_blank">IKEA Matter Compatible System</a>, or other devices linked up to the Matter hub. Then, you can link it up so when the weather's hot, it'll connect to other products like the <a href="https://www.ikea.com/us/en/p/myggbett-door-window-sensor-smart-60617641/" target="_blank">MYGGBETT door/window sensor</a> to tell you if you need to go and shut or open up to let some fresh air in.</p><div class="product"><a data-dimension112="d72e006c-0615-4296-a64f-0577737630d0" data-action="Deal Block" data-label="This handy and small smart home sensor measures up at 2 1/2-inch (W) x 3/4-inch (H) meaning it'll place subtly on any wall around your home. It's worth noting though that it runs on two AAA batteries, which you'll need to buy separately. But, at just under $10 it's a very affordable smart home device." data-dimension48="This handy and small smart home sensor measures up at 2 1/2-inch (W) x 3/4-inch (H) meaning it'll place subtly on any wall around your home. It's worth noting though that it runs on two AAA batteries, which you'll need to buy separately. But, at just under $10 it's a very affordable smart home device." data-dimension25="$9.99" href="https://www.ikea.com/us/en/p/timmerflotte-temperature-humidity-sensor-smart-50618957/" 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="TSdkguPwDkAun8qX8FNFnm" name="timmerflotte_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/TSdkguPwDkAun8qX8FNFnm.jpg" mos="" align="middle" fullscreen="" width="535" height="535" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This handy and small smart home sensor measures up at 2 1/2-inch (W) x 3/4-inch (H) meaning it'll place subtly on any wall around your home. It's worth noting though that it runs on two AAA batteries, which you'll need to buy separately. But, at just under $10 it's a very affordable smart home device.<a class="view-deal button" href="https://www.ikea.com/us/en/p/timmerflotte-temperature-humidity-sensor-smart-50618957/" target="_blank" rel="nofollow" data-dimension112="d72e006c-0615-4296-a64f-0577737630d0" data-action="Deal Block" data-label="This handy and small smart home sensor measures up at 2 1/2-inch (W) x 3/4-inch (H) meaning it'll place subtly on any wall around your home. It's worth noting though that it runs on two AAA batteries, which you'll need to buy separately. But, at just under $10 it's a very affordable smart home device." data-dimension48="This handy and small smart home sensor measures up at 2 1/2-inch (W) x 3/4-inch (H) meaning it'll place subtly on any wall around your home. It's worth noting though that it runs on two AAA batteries, which you'll need to buy separately. But, at just under $10 it's a very affordable smart home device." data-dimension25="$9.99">View Deal</a></p></div><h2 id="how-to-use-it">How to use 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="P4gRXburf2j85BLs2saDAN" name="timmerflotte_2" alt="The IKEA TIMMERFLOTTE device on a wall" src="https://cdn.mos.cms.futurecdn.net/P4gRXburf2j85BLs2saDAN.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: IKEA)</span></figcaption></figure><p>The IKEA TIMMERFLOTTE temperature and humidity sensor is a simple device used to keep a check on the climate inside your home, which is a super helpful tool especially when it's getting hot outside.</p><p>When <a href="https://www.tomsguide.com/home/smart-home/i-tested-ikeas-new-smart-home-devices-and-these-are-the-2-id-actually-buy">we reviewed some of IKEA's new smart home devices</a>, we noted that the TIMMERFLOTTE's all-white face shows a readout of either the temperature or humidity and to swap between the two, you simply press it. We also added: "Since the LEDs are white, it can be a bit hard to read if you're in direct sunlight."</p><p>As long as you attach it to the wall, as per the assembly instructions, out of any immediate sunlight, it'll be easier to read and a big help when you want to know how hot your home s getting.</p><p>But why it's really great for tackling the heatwave is that it's part of IKEA's smart home range. This device is built on the open standard Matter, compatible with smart systems like Amazon, Google and Samsung. </p><p>Specifically, it uses Matter over Thread, which means you need a Thread Border Router to control it through your phone, like <a href="https://www.ikea.com/us/en/p/dirigera-hub-for-smart-products-white-smart-50503414/" target="_blank">IKEA's DIRIGERA hub</a>. Link it up to a Matter-compatible fan or AC unit and you've got yourself a handy tool to track the temperature and then do something about it.</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/im-an-interiors-expert-and-these-are-the-5-things-i-stock-up-on-every-time-i-go-to-ikea">I'm an interiors expert, and these are the 5 things I stock up on every time I go to IKEA</a></li><li><a href="https://www.tomsguide.com/home/outdoors/this-ikea-storage-essential-is-ideal-for-family-camping-trips-and-its-only-usd10">This IKEA storage essential is ideal for family camping trips, and it's only $10</a></li><li><a href="https://www.tomsguide.com/home/i-found-this-ikea-tool-in-the-kids-section-but-its-been-a-game-changer-for-my-productivity">I found this IKEA tool in the kids' section, but it's been a game-changer for my productivity</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Why marigolds are the best companion flower for a pest-free vegetable garden (especially tomatoes) ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/home/gardening/why-marigolds-are-the-best-companion-flower-for-a-pest-free-vegetable-garden-especially-tomatoes</link>
                                                                            <description>
                            <![CDATA[ Learn how to plant marigolds to protect your tomatoes and other vegetables from common garden pests. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">nkgmKdmRfFwpWtcXh732f8</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/T8XqceMC5PSVk25CDDQVHi-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 05 Jun 2026 05:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gardening]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Outdoors]]></category>
                                                                                                <author><![CDATA[ kaycee.hill@futurenet.com (Kaycee Hill) ]]></author>                    <dc:creator><![CDATA[ Kaycee Hill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xHn6RmpEqg87cvtLwrBu9G.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/T8XqceMC5PSVk25CDDQVHi-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Marigolds next to tomatoes in vegetable garden]]></media:description>                                                            <media:text><![CDATA[Marigolds next to tomatoes in vegetable garden]]></media:text>
                                <media:title type="plain"><![CDATA[Marigolds next to tomatoes in vegetable garden]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/T8XqceMC5PSVk25CDDQVHi-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you're like most gardeners, your tomatoes are taking off right now, which means the pests are on their way. Whiteflies, hornworms, root-knot nematodes, and spider mites are already hunting for tender tomato foliage and juicy fruit.</p><p>Fortunately, you can defend your entire vegetable garden with a single companion plant: marigolds.</p><p>Marigolds aren't just pretty flowers. They're pest deterrents, root protectors, and pollinator magnets that work double duty protecting both your tomatoes and surrounding vegetables. </p><p>Here's how to use them strategically throughout your garden for maximum pest protection and yield.</p><section class="howto-block">                    <h3>1. Mask vegetable scents to confuse pests</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/xt5rR8hq8Nbo7sfRHf2Sb8.jpg"                                        alt="A tomato plant being watered by a watering can"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/xt5rR8hq8Nbo7sfRHf2Sb8.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Marigolds produce a distinct odor which masks the smell of your vegetables. Pests like whiteflies use scent to locate plants, they smell tomato foliage and navigate straight to it. Marigolds disrupt this process by creating olfactory confusion.</p><p><strong>Plant marigolds around the perimeter of your tomato patch and interspersed among other vegetables like squash, peppers, and cabbage</strong>. The marigold scent overwhelms the vegetable scents, making it harder for pests to find their preferred targets.</p><p>Whiteflies are especially susceptible to this trick. They arrive looking for soft tomato leaves to feed on, but the marigold odor throws them off the trail. <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC6396911/" target="_blank">Research shows</a>  strong-smelling companion plants reduce whitefly damage by up to 50% on tomatoes.</p></p>                </section><section class="howto-block">                    <h3>2. Protect roots from underground threats</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/za6QNQsyG3xNXHgNaUwX9i.jpg"                                        alt="Marigold flowers"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/za6QNQsyG3xNXHgNaUwX9i.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Root-knot nematodes are a tomato grower's underground nightmare. These microscopic roundworms burrow into roots, causing severe swelling that chokes off water and nutrients, leading to stunted growth and ruined yields. </p><p>While gardening lore often claims just a single marigold can create a protective barrier, the real science is a bit more nuanced: marigolds don't actually repel nematodes from a distance, but rather act as a "trap crop." </p><p>Because of this trap-crop mechanism, <strong>the golden rule of marigold protection is density</strong>. Planting just one or two lonely marigolds leaves too many unprotected gaps where nematodes can still find your vulnerable tomato, pepper, or squash roots.</p><p>To build an effective underground defensive web, <strong>plant as many French marigolds as you can pack into the bed</strong>, surrounding your vegetables in a dense, continuous ring of golden blooms.</p></p>                </section><div class="product"><a data-dimension112="57941a77-0158-40c2-8e7d-c026d714b4a9" data-action="Deal Block" data-label="Skip the weeks of waiting for seeds to sprout. These live plants are ready to go straight into the ground next to your tomatoes, instantly releasing those pest-repelling scents your garden needs right now." data-dimension48="Skip the weeks of waiting for seeds to sprout. These live plants are ready to go straight into the ground next to your tomatoes, instantly releasing those pest-repelling scents your garden needs right now." data-dimension25="$19" href="https://www.amazon.com/Live-Flowering-Marigold-Assorted-Beautiful/dp/B0H1KZHCV8" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2002px;"><p class="vanilla-image-block" style="padding-top:86.61%;"><img id="2rrGbHr7ydi8y7BiTSRaX" name="Live assorted marigolds" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/2rrGbHr7ydi8y7BiTSRaX.png" mos="" align="middle" fullscreen="" width="2002" height="1734" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Skip the weeks of waiting for seeds to sprout. These live plants are ready to go straight into the ground next to your tomatoes, instantly releasing those pest-repelling scents your garden needs right now. <a class="view-deal button" href="https://www.amazon.com/Live-Flowering-Marigold-Assorted-Beautiful/dp/B0H1KZHCV8" target="_blank" rel="nofollow" data-dimension112="57941a77-0158-40c2-8e7d-c026d714b4a9" data-action="Deal Block" data-label="Skip the weeks of waiting for seeds to sprout. These live plants are ready to go straight into the ground next to your tomatoes, instantly releasing those pest-repelling scents your garden needs right now." data-dimension48="Skip the weeks of waiting for seeds to sprout. These live plants are ready to go straight into the ground next to your tomatoes, instantly releasing those pest-repelling scents your garden needs right now." data-dimension25="$19">View Deal</a></p></div><section class="howto-block">                    <h3>3. Attract beneficial insects</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/4Hy6SEuh5rGQNKkRzHFWoY.jpg"                                        alt="A closeup of a ladbyug eating an aphid"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/4Hy6SEuh5rGQNKkRzHFWoY.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Marigold flowers attract ladybugs, lacewings, and other beneficial insects that prey on common garden pests. These natural predators feed on whiteflies, spider mites, and aphids, which are all major tomato threats.</p><p>Ladybugs are especially valuable. A single ladybug eats 5,000 aphids in its lifetime. By planting marigolds, you're essentially hiring free, 24/7 pest control. </p><p><strong>Plant marigolds in clusters near your tomato cages and vegetable beds</strong>. The flowers attract beneficials which then patrol your vegetables looking for pests. This creates a self-sustaining pest management system without chemical intervention.</p></p>                </section><section class="howto-block">                    <h3>4. Boost yields through increased pollination</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/DJhsuJZWY9US5Zp6Qq7i9c.jpg"                                        alt="Orange marigolds"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/DJhsuJZWY9US5Zp6Qq7i9c.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Marigolds attract bees relentlessly. More bees mean better pollination of tomato flowers, which directly translates to heavier fruit production. Gardens with marigolds consistently produce juicier, heavier tomatoes than gardens without them.</p><p>The pollination boost extends to squash, peppers, and other vegetables which rely on bees. More pollinators visiting your garden means better fertilization across all your crops and significantly higher yields overall.</p><p><strong>Plant marigolds near tomato flowers so bees visiting marigolds also visit nearby tomato blooms</strong>. Even modest increases in pollination activity result in noticeably better fruit set.</p></p>                </section><div class="product"><a data-dimension112="ec0344f9-cb3e-46ac-b874-e1ec8c5a127d" data-action="Deal Block" data-label="This collection gives you the ultimate mix of sizes and pest-repelling power. They sprout fast (5–12 days) and bloom continuously from summer through fall, creating a long-lasting, pollinator-friendly barrier against garden pests." data-dimension48="This collection gives you the ultimate mix of sizes and pest-repelling power. They sprout fast (5–12 days) and bloom continuously from summer through fall, creating a long-lasting, pollinator-friendly barrier against garden pests." data-dimension25="$6" href="https://www.amazon.com/dp/B0D63MJBFL" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2106px;"><p class="vanilla-image-block" style="padding-top:81.20%;"><img id="DTTmBAhCrckZaLdB7xBqZd" name="Sow Right Seeds - Marigold Collection Flower Seeds for Planting" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/DTTmBAhCrckZaLdB7xBqZd.png" mos="" align="middle" fullscreen="" width="2106" height="1710" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This collection gives you the ultimate mix of sizes and pest-repelling power. They sprout fast (5–12 days) and bloom continuously from summer through fall, creating a long-lasting, pollinator-friendly barrier against garden pests.<a class="view-deal button" href="https://www.amazon.com/dp/B0D63MJBFL" target="_blank" rel="nofollow" data-dimension112="ec0344f9-cb3e-46ac-b874-e1ec8c5a127d" data-action="Deal Block" data-label="This collection gives you the ultimate mix of sizes and pest-repelling power. They sprout fast (5–12 days) and bloom continuously from summer through fall, creating a long-lasting, pollinator-friendly barrier against garden pests." data-dimension48="This collection gives you the ultimate mix of sizes and pest-repelling power. They sprout fast (5–12 days) and bloom continuously from summer through fall, creating a long-lasting, pollinator-friendly barrier against garden pests." data-dimension25="$6">View Deal</a></p></div><section class="howto-block">                    <h3>5. Position marigolds strategically</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/PSUw2CLaAQeZSVeUE3ALpY.jpg"                                        alt="Growing tomatoes in raised garden bed with marigolds "                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/PSUw2CLaAQeZSVeUE3ALpY.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>Location matters. Marigolds thrive in full sun, just like tomatoes. But plant them too close and they'll compete for sunlight, water, and nutrients.</p><p><strong>Plant marigolds 1-2 feet away from tomato cages so they don't get shaded by your tomato plants as they grow taller</strong>. This spacing keeps both plants in full sun while maintaining the pest-deterrent benefits.</p><p><strong>Space marigolds around the perimeter of your vegetable garden and between plants in beds</strong>. This creates layers of protection—pests encounter marigolds before reaching your tomatoes and other vegetables.</p><p>You can also create visual interest by planting different marigold varieties. Marigold African Tall reaches 3 feet and creates height variation. Signet marigolds stay compact at one foot and work between closer plantings. </p><p>Having a mix of varieties not only provides aesthetic appeal to your yard, but functional coverage too. </p></p>                </section><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OarKpX"></div>                            </div>                            <script src="https://kwizly.com/embed/OarKpX.js" async></script><p>Got a secret weapon or hack for dealing with your garden struggles? <strong>Drop it in the comments below!</strong></p><figure class="van-image-figure pull-right inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:676px;"><p class="vanilla-image-block" style="padding-top:31.51%;"><img id="ZCwuUFRE3YeboHR4zyEWmb" name="r3t8zZ5ve4GewFTeoCM3R6" alt="Google" src="https://cdn.mos.cms.futurecdn.net/ZCwuUFRE3YeboHR4zyEWmb.jpg" mos="" align="right" fullscreen="" width="676" height="213" attribution="" endorsement="" class="pull-rightinline"></p></div></div><figcaption itemprop="caption description" class="pull-right inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></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/gardening/why-are-my-tomato-plant-leaves-turning-yellow-5-steps-to-save-your-harvest">Why are my tomato plant leaves turning yellow? 5 steps to save your harvest</a></li><li><a href="https://www.tomsguide.com/home/gardening/when-to-fertilize-your-tomatoes-and-how-to-get-a-bountiful-harvest">When to fertilize tomatoes: the 3-step schedule for a bountiful harvest</a></li><li><a href="https://www.tomsguide.com/home/outdoors/gardeners-swear-by-this-simple-trick-heres-how-to-save-split-tomatoes-fast">Gardeners swear by this simple trick — here's how to save split tomatoes fast</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
            </channel>
</rss>