<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:dcterms="http://purl.org/dc/terms/"
     xmlns:media="http://search.yahoo.com/mrss/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:cf="https://www.futureplc.com/rss/content-flags"
>
    <channel>
                    <atom:link href="https://www.tomsguide.com/feeds/tag/sony" rel="self" type="application/rss+xml" />
                            <title><![CDATA[ Latest from Tom's Guide in Sony ]]></title>
                <link>https://www.tomsguide.com/tag/sony</link>
        <description><![CDATA[ All the latest sony content from the Tom's Guide team ]]></description>
                                    <lastBuildDate>Fri, 18 Sep 2026 06:00:00 +0000</lastBuildDate>
                            <language>en</language>
                                <item>
                                                            <title><![CDATA[ Sony Bravia 3 II vs Hisense U6SF Pro: After testing both TVs, one comes out on top ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-3-ii-review">Sony Bravia 3 II</a> is one of the most affordable Bravia-branded TVs in 2026. It comes with a remarkably thorough collection of features given its place in Sony's Bravia TV lineup, but its relatively simplistic display hardware and lack of brightness limit its versatility.</p><p>As its name suggests, the <a href="https://www.tomsguide.com/tvs/qled-tvs/hisense-u6sf-pro-review">Hisense U6SF Pro</a> is an entry-level TV with a little extra flair compared to garden-variety, budget-friendly TVs. Its Mini-LED backlighting is the key to its success, and it's one of the brightest sets in its class. This makes it a dependable choice for a wide swath of shoppers.</p><p>In the battle between two affordable, high-performance LED TVs, which one is right for you? Before you make a decision, there are some key differences worth highlighting. We've tested both models, and we're happy to point you in the right direction.</p>        <div class="featured_product_block featured_block_versus" data-id="8bf7bdc6-b2a7-11f1-a274-113dcc4b9dcc">            <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-3-ii-review" data-model-name="Sony Bravia 3 II" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:56.25%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/2V8bVypAPQM7VEmWdNX5xh.jpg" alt="Sony Bravia 3 II LED TV"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Sony Bravia 3 II</div>                                    </div>                <div class="subtitle__description">                                <div class="stars__reviews"><span itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating" class="chunk rating"><span class="icon icon-star"> </span><span class="icon icon-star"> </span><span class="icon icon-star"> </span><span class="icon icon-star"> </span><meta itemprop="bestRating" content="100.0" /><meta itemprop="worstRating" content="0.0" /><meta itemprop="ratingValue" content="80" /></span></div>                                        <p><p>Although it leads the affordable LED TV class in features, the Sony Bravia 3 II isn't equipped with the display hardware it needs to compete with the brightness and backlight control of a TV like the Hisense U6SF Pro. Sony's picture processing does a splendid job of making the most of the Bravia 3 II's direct-LED display, but the U6SF Pro's Mini-LEDs give it a significant advantage.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_versus" data-id="8bf7be52-b2a7-11f1-a219-8f23a6b61de9">            <a href="https://www.tomsguide.com/tvs/qled-tvs/hisense-u6sf-pro-review" data-model-name="Hisense U6SF Pro" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:56.25%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rfz9ciproKTVqNsfWRmLsU.jpg" alt="Hisense U6SF Pro"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Hisense U6SF Pro</div>                                    </div>                <div class="subtitle__description">                                <div class="stars__reviews"><span itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating" class="chunk rating"><span class="icon icon-star"> </span><span class="icon icon-star"> </span><span class="icon icon-star"> </span><span class="icon icon-star"> </span><span class="icon icon-star half"></span><meta itemprop="bestRating" content="100.0" /><meta itemprop="worstRating" content="0.0" /><meta itemprop="ratingValue" content="90" /></span></div>                                        <p><p>The Hisense U6SF Pro is an entry-level Mini-LED TV with added <em>oomph</em>. It's one of the brightest TVs you'll find in its price range, which makes it a great fit for all viewing scenarios. It's not quite as flush with features as the Bravia 3 II, comes in fewer sizes and relies on Fire TV rather than Google TV, but its versatility makes it a better choice for most shoppers.</p></p>                </div>                            </div>        </div><h2 id="sony-bravia-3-ii-vs-hisense-u6sf-pro-specs-compared">Sony Bravia 3 II vs Hisense U6SF Pro: Specs compared</h2><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p>Sony Bravia 3 II</p></th><th  ><p>Hisense U6SF Pro</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Sizes</strong></p></td><td  ><p>43", 50", 55", 65", 75", 85", 100"</p></td><td  ><p>55", 65", 75", 85", 100"</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>4x HDMI 2.1</p></td><td  ><p>2x HDMI 2.1, 2x HDMI 2.0</p></td></tr><tr><td class="firstcol " ><p><strong>Resolution</strong></p></td><td  ><p>3,840 x 2,160p</p></td><td  ><p>3,840 x 2,160p</p></td></tr><tr><td class="firstcol " ><p><strong>Refresh rate</strong></p></td><td  ><p>120Hz</p></td><td  ><p>144Hz</p></td></tr><tr><td class="firstcol " ><p><strong>HDR</strong></p></td><td  ><p>Dolby Vision, HDR10, HLG</p></td><td  ><p>Dolby Vision, HDR10+, HDR10, HLG</p></td></tr><tr><td class="firstcol " ><p><strong>Smart TV software</strong></p></td><td  ><p>Google TV</p></td><td  ><p>Fire TV</p></td></tr><tr><td class="firstcol " ><p><strong>ATSC 3.0 support?</strong></p></td><td  ><p>No</p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>XR Processor</p></td><td  ><p>Hi-View AI Engine</p></td></tr></tbody></table></div><p>Despite its entry-level status, the Bravia 3 II's spec sheet is one of the best of I've seen on a Sony TV in recent years, and it all starts with its full slate of HDMI 2.1-compatible inputs. Until now, it's been tricky to track down a Sony TV with more than two high-bandwidth ports.</p><p>Whichever HDMI port you pick, it's ready to make the most of your connected device. All four support 4K games at 120Hz — the top 4K spec on current-generation consoles.</p><p>The Hisense U6SF Pro, meanwhile, supports 4K content up to 144Hz, so it's slightly more accommodating for people who plan on linking up a gaming PC. However, only two of its HDMI ports support this functionality, so the Bravia 3 II's port selection is better equipped for a dedicated gamers.</p><p>Another key advantage of the Bravia 3 II is its wider array of size options. It's available in both 43- and 50-inch screen sizes, which is relatively hard to come by in its class.</p><p>The U6SF Pro's HDR10+ support is nice to have, but the added size options in the Bravia 3 II lineup and its universal HDMI 2.1 support put it over the top.</p><p><em><strong>Winner: </strong></em><em>Sony Bravia 3 II</em></p><h2 id="sony-bravia-3-ii-vs-hisense-u6sf-pro-design">Sony Bravia 3 II vs Hisense U6SF Pro: Design</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.15%;"><img id="uBGFHiMMLgDXDsogk9jYe5" name="Sony-Bravia-3-II--19" alt="The back of a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/uBGFHiMMLgDXDsogk9jYe5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Neither of these budget-friendly TVs are dressed as dashingly as higher-end models. However, given their place in each brand's respective lineup, they <em>could</em> look and feel a lot worse than they do.</p><p>The Sony Bravia 3 II (pictured above) leverages a sturdy panel that looks about as utilitarian as a TV gets. That said, I appreciate the minimalist approach, as the Bravia 3 II is able to slot right into any living room without looking aesthetically out of place.</p><p>At close to three inches thick, the Bravia 3 II's panel is a bit of a <em>chonkster. </em>If you're expecting something slim, you might want to take a look at it in person before plunking down your credit card. In any event, the U6SF Pro isn't much thinner.</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="zx6r6rPCcxxc86JB4XmKfn" name="Hisense-65U6SF-Pro-2026--18" alt="The back of a Hisense U6SF Pro TV" src="https://cdn.mos.cms.futurecdn.net/zx6r6rPCcxxc86JB4XmKfn-1920-80.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>Take a trip around the back of the U6SF Pro (seen above) and you'll notice that its panel has a bit more business going on — lines, indents, things of that nature. Most notably is its built-in subwoofer, which according to my colleague Dylan Haas, does a pretty decent job of filling out the TV's soundscape.</p><p>Both TV's screens sit somewhat low to the ground, but the Bravia 3 II offers a bit more soundbar clearance. In addition, I have to shout out the remote control included with the Bravia 3 II, which is much more pleasant to hold and use than the clicker that comes with the U6SF Pro.</p><p>But when sizing up both sets, their design chops are pretty much a wash. They'll look fine in all rooms and don't feel cheap to the touch.</p><p><em><strong>Winner:</strong></em><em>  Draw</em></p><h2 id="sony-bravia-3-ii-vs-hisense-u6sf-pro-performance">Sony Bravia 3 II vs Hisense U6SF Pro: Performance</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="ZUsGHhLnyFsvwLyudVXtUo" name="Hisense-65U6SF-Pro-2026--12" alt="Hisense U6SF Pro TV on a desk" src="https://cdn.mos.cms.futurecdn.net/ZUsGHhLnyFsvwLyudVXtUo-1920-80.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>Before we dig deeper into how these TVs look, let's compare their test results. Both TVs were in their most-accurate picture modes when these tests were taken. (For the Bravia 3, this is Professional mode. For the U6SF Pro, it's Filmmaker mode.)</p><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>Sony Bravia 3 II</strong></p></th><th  ><p><strong>Hisense U6SF Pro</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>SDR Brightness (10%, in nits)    </strong></p></td><td  ><p>352</p></td><td  ><p>358</p></td></tr><tr><td class="firstcol " ><p><strong>Delta-E (lower is better)    </strong></p></td><td  ><p>2.1</p></td><td  ><p>1.6</p></td></tr><tr><td class="firstcol " ><p><strong>HDR Brightness (10%, in nits)    </strong></p></td><td  ><p>520</p></td><td  ><p>1,265</p></td></tr><tr><td class="firstcol " ><p><strong>UHDA-P3 Gamut Coverage    </strong></p></td><td  ><p>94.62%</p></td><td  ><p>96.1%</p></td></tr><tr><td class="firstcol " ><p><strong>Rec.2020 Gamut Coverage    </strong></p></td><td  ><p>74.06% </p></td><td  ><p>75.12%</p></td></tr><tr><td class="firstcol " ><p><strong>Input latency (milliseconds)   </strong></p></td><td  ><p>9.8</p></td><td  ><p>9.3</p></td></tr></tbody></table></div><p>The U6SF Pro's primary advantage over the Bravia 3 II is its display hardware. It taps an array of Mini-LEDs that can be brightened and dimmed across multiple zones, while Sony's entry-level set is limited to standard, direct-LED backlighting with no local dimming at all.</p><p>The result — as is illustrated in the chart above — is that the U6SF Pro features a <em>much</em> brighter presentation. This is especially true for HDR content, where Mini-LEDs really shine.</p><p>Additionally, the U6SF Pro's smaller-sized LEDs and local dimming capabilities allow for tighter contrast control. Black levels are deeper than they are on the Bravia 3 II during certain content, which contributes to a more cinematic picture overall.</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.15%;"><img id="z9qpiWtks64ixxEWPG5Mb5" name="Sony-Bravia-3-II--7" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/z9qpiWtks64ixxEWPG5Mb5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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><div><blockquote><p>Our eyes have evolved to notice contrast above almost everything else.</p></blockquote></div><p>To its credit, the Bravia 3 II doesn't sport a bad-looking picture. True to Sony's reputation, the Bravia 3 II delivers a dialed-in presentation in its most-accurate picture mode. It handles the finer aspects of picture quality (like motion handling and upscaling) better than most entry-level sets.</p><p>But while Hisense's picture processing isn't as impressive as Sony's, our eyes have evolved to notice contrast above almost everything else. That the U6SF Pro is about twice as bright as the Bravia 3 II is what makes it the better choice for HDR content, bright-room viewing and with the lights off.</p><p>Colors look brighter on the U6SF Pro, too, as a result of this added luminance. When you factor this alongside the Bravia 3 II's limited contrast, you're left with a picture that runs the risk of looking flat in certain lighting conditions.</p><p><em><strong>Winner:</strong></em><em> Hisense U6SF Pro</em></p><h2 id="sony-bravia-3-ii-vs-hisense-u6sf-pro-smart-platform-and-features">Sony Bravia 3 II vs Hisense U6SF Pro: Smart platform and features</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.15%;"><img id="DypMFjKQD8twXfKohCrNi5" name="Sony-Bravia-3-II--10" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/DypMFjKQD8twXfKohCrNi5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Like all Bravia TVs, the Bravia 3 II is built around the Google TV smart platform. It's not my first choice when it comes to streaming software, but it's good enough to serve as a your daily viewing backdrop.</p><p>The Google TV interface is, for the most part, laid out in a sensible way, and while plenty of ads grace the home screen, it's not difficult to prioritize the apps and shows you're most likely to engage with. Google TV is also connected to a huge library of downloadable apps, so you'll rarely find yourself wondering why a piece of software isn't supported.</p><p>When it comes to bells and whistles, there's a lot to enjoy. The Bravia 3 II offers gamers Variable Refresh Rate (VRR) and Auto Low Latency Mode (ALLM), as well as the aforementioned quartet of HDMI 2.1 inputs. IMAX Enhanced mode is along for the ride, too, as well as all of the usual Google TV enhancements (like Chromecast functionality).</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="WyyAzYmMYzEc974dXHiZzn" name="Hisense-65U6SF-Pro-2026--14" alt="Hisense U6SF Pro TV on a desk" src="https://cdn.mos.cms.futurecdn.net/WyyAzYmMYzEc974dXHiZzn-1920-80.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>Unlike most mainline Hisense TVs in 2026, the U6SF Pro <em>isn't</em> running Google TV. Instead, it's built around Amazon's smart platform, Fire TV.</p><p>While Fire TV has seen some improvements in recent years, I still find the overall experience to be rather frustrating. At times it can be slow to boot up apps, and its user interface isn't as streamlined as that of Roku and Google TV. Unsurprisingly, folks who already use a number of Alexa-enabled devices in their home stand to make the most out of Fire TV.</p><p>Similar to the Bravia 3 II, the U6SF Pro also supports VRR, ALLM and IMAX Enhanced mode. It also advertises AMD FreeSync Premium Pro. The U6SF Pro <em>would be</em> a slightly better gaming TV than the Bravia 3 II if it offered as many HDMI 2.1-enabled ports.</p><p><em><strong>Winner:  </strong></em><em>Sony Bravia 3 II</em></p><h2 id="sony-bravia-3-ii-vs-hisense-u6sf-pro-verdict">Sony Bravia 3 II vs Hisense U6SF Pro: Verdict</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:42.85%;"><img id="gMvyKdpRaHv6kzjtCN4K8" name="Hisense-65U6SF-Pro-2026--6-HERO" alt="Hisense U6SF Pro TV on a desk" src="https://cdn.mos.cms.futurecdn.net/gMvyKdpRaHv6kzjtCN4K8-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="857" 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><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p>Sony Bravia 3 II</p></th><th  ><p>Hisense U6SF Pro</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Specs (25)</strong></p></td><td  ><p>23</p></td><td  ><p>22</p></td></tr><tr><td class="firstcol " ><p><strong>Design (25)</strong></p></td><td  ><p>21</p></td><td  ><p>21</p></td></tr><tr><td class="firstcol " ><p><strong>Performance (25)</strong></p></td><td  ><p>18</p></td><td  ><p>22</p></td></tr><tr><td class="firstcol " ><p><strong>Features (25)</strong></p></td><td  ><p>23</p></td><td  ><p>22</p></td></tr><tr><td class="firstcol " ><p><strong>Total Score (100)</strong></p></td><td  ><p><strong>85</strong></p></td><td  ><p><strong>87</strong></p></td></tr></tbody></table></div><p>The U6SF Pro is a safer pick for a wider variety of viewers than the Bravia 3 II. The reason for that is simple: Its picture is about twice as bright.</p><p>Thanks to its impressive picture processing, Google TV and the inclusion of four HDMI 2.1 inputs, an argument could easily be made that the Bravia 3 II is the best direct-LED-based TV on the market. It's certainly a fantastic gaming TV for the price, at least.</p><p>But the U6SF Pro's Mini-LED backlighting and local dimming are more impactful on a day-to-day basis than the Bravia 3's extra pair of high-bandwidth inputs, and the same goes for Google TV. The ideal Bravia 3 II-owner is someone who owns several consoles, appreciates the finer aspects of picture quality and does most of their viewing during the day (in a room that isn't <em>too</em> bright). That's a pretty narrow slice.</p><p>And we haven't even talked about pricing yet. The <a href="https://www.bestbuy.com/product/hisense-65-class-u6-pro-miniled-qled-uhd-4k-hdr-glare-free-smart-fire-tv-with-alexa-2026/J3Z9Z42WZZ" target="_blank" rel="nofollow">65-inch Hisense U6SF Pro is $699 at Best Buy</a> at the time of publishing — it's not the cheapest set, but you're getting a lot for that price.</p><p>Meanwhile, the <a href="https://www.bestbuy.com/product/sony-65-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-and-anti-glare-screen-2026/J7XSRH54PY" target="_blank" rel="nofollow">65-inch Bravia 3 II is currently $899 at Best Buy</a>. Unless you're absolutely certain that the Bravia 3 II's performance profile is a good fit for your living room, I recommend taking a closer look at the set with Mini-LED backlighting.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-WQA6zW"></div>                            </div>                            <script src="https://kwizly.com/embed/WQA6zW.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/tvs/4k-tvs/sony-bravia-3-ii-review">Sony Bravia 3 II review</a></li><li><a href="https://www.tomsguide.com/tvs/qled-tvs/hisense-u6sf-pro-review">Hisense U6SF Pro review</a></li><li><a href="https://www.tomsguide.com/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up">Best TV Brands 2026: We just graded them on price, performance and more — here’s how their report cards stack up</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-3-ii-vs-hisense-u6sf-pro-after-testing-both-tvs-one-comes-out-on-top</link>
                                                                            <description>
                            <![CDATA[ The Sony Bravia 3 II and the Hisense U6SF Pro are among the best affordable TVs of 2026. Here's how they compare — from performance to features and price. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">jUugZrYFFCM8chKxiz4cuS</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/YeFTKQLk6u8jLNhVuuynKk-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 18 Sep 2026 06:00:00 +0000</pubDate>                                                                                                                                <updated>Fri, 18 Sep 2026 07:52:09 +0000</updated>
                                                                                                                                            <category><![CDATA[4k TVs]]></category>
                                                    <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ michael.desjardin@futurenet.com (Michael Desjardin) ]]></author>                    <dc:creator><![CDATA[ Michael Desjardin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/esytxRqW9GNVZNVCf7BFyV-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael Desjardin is a Senior Editor for TVs at Tom&#039;s Guide. He&#039;s been testing and tinkering with TVs professionally for over a decade, previously for Reviewed and USA Today. Michael studied media production and screenwriting at Emerson College. He loves cooking, zoning out to ambient music, and getting way too invested in the Red Sox. He considers himself living proof that TV doesn&#039;t necessarily rot your brain.&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/YeFTKQLk6u8jLNhVuuynKk-1920-80.jpg">
                                                            <media:credit><![CDATA[Sony / Hisense / Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Sony Bravia 3 II vs Hisense U6SF Pro on a colorful background.]]></media:description>                                                            <media:text><![CDATA[The Sony Bravia 3 II vs Hisense U6SF Pro on a colorful background.]]></media:text>
                                <media:title type="plain"><![CDATA[The Sony Bravia 3 II vs Hisense U6SF Pro on a colorful background.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/YeFTKQLk6u8jLNhVuuynKk-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-3-ii-review">Sony Bravia 3 II</a> is one of the most affordable Bravia-branded TVs in 2026. It comes with a remarkably thorough collection of features given its place in Sony's Bravia TV lineup, but its relatively simplistic display hardware and lack of brightness limit its versatility.</p><p>As its name suggests, the <a href="https://www.tomsguide.com/tvs/qled-tvs/hisense-u6sf-pro-review">Hisense U6SF Pro</a> is an entry-level TV with a little extra flair compared to garden-variety, budget-friendly TVs. Its Mini-LED backlighting is the key to its success, and it's one of the brightest sets in its class. This makes it a dependable choice for a wide swath of shoppers.</p><p>In the battle between two affordable, high-performance LED TVs, which one is right for you? Before you make a decision, there are some key differences worth highlighting. We've tested both models, and we're happy to point you in the right direction.</p>        <div class="featured_product_block featured_block_versus" data-id="8bf7bdc6-b2a7-11f1-a274-113dcc4b9dcc">            <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-3-ii-review" data-model-name="Sony Bravia 3 II" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:56.25%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/2V8bVypAPQM7VEmWdNX5xh.jpg" alt="Sony Bravia 3 II LED TV"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Sony Bravia 3 II</div>                                    </div>                <div class="subtitle__description">                                <div class="stars__reviews"><span itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating" class="chunk rating"><span class="icon icon-star"> </span><span class="icon icon-star"> </span><span class="icon icon-star"> </span><span class="icon icon-star"> </span><meta itemprop="bestRating" content="100.0" /><meta itemprop="worstRating" content="0.0" /><meta itemprop="ratingValue" content="80" /></span></div>                                        <p><p>Although it leads the affordable LED TV class in features, the Sony Bravia 3 II isn't equipped with the display hardware it needs to compete with the brightness and backlight control of a TV like the Hisense U6SF Pro. Sony's picture processing does a splendid job of making the most of the Bravia 3 II's direct-LED display, but the U6SF Pro's Mini-LEDs give it a significant advantage.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_versus" data-id="8bf7be52-b2a7-11f1-a219-8f23a6b61de9">            <a href="https://www.tomsguide.com/tvs/qled-tvs/hisense-u6sf-pro-review" data-model-name="Hisense U6SF Pro" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:56.25%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rfz9ciproKTVqNsfWRmLsU.jpg" alt="Hisense U6SF Pro"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Hisense U6SF Pro</div>                                    </div>                <div class="subtitle__description">                                <div class="stars__reviews"><span itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating" class="chunk rating"><span class="icon icon-star"> </span><span class="icon icon-star"> </span><span class="icon icon-star"> </span><span class="icon icon-star"> </span><span class="icon icon-star half"></span><meta itemprop="bestRating" content="100.0" /><meta itemprop="worstRating" content="0.0" /><meta itemprop="ratingValue" content="90" /></span></div>                                        <p><p>The Hisense U6SF Pro is an entry-level Mini-LED TV with added <em>oomph</em>. It's one of the brightest TVs you'll find in its price range, which makes it a great fit for all viewing scenarios. It's not quite as flush with features as the Bravia 3 II, comes in fewer sizes and relies on Fire TV rather than Google TV, but its versatility makes it a better choice for most shoppers.</p></p>                </div>                            </div>        </div><h2 id="sony-bravia-3-ii-vs-hisense-u6sf-pro-specs-compared">Sony Bravia 3 II vs Hisense U6SF Pro: Specs compared</h2><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p>Sony Bravia 3 II</p></th><th  ><p>Hisense U6SF Pro</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Sizes</strong></p></td><td  ><p>43", 50", 55", 65", 75", 85", 100"</p></td><td  ><p>55", 65", 75", 85", 100"</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>4x HDMI 2.1</p></td><td  ><p>2x HDMI 2.1, 2x HDMI 2.0</p></td></tr><tr><td class="firstcol " ><p><strong>Resolution</strong></p></td><td  ><p>3,840 x 2,160p</p></td><td  ><p>3,840 x 2,160p</p></td></tr><tr><td class="firstcol " ><p><strong>Refresh rate</strong></p></td><td  ><p>120Hz</p></td><td  ><p>144Hz</p></td></tr><tr><td class="firstcol " ><p><strong>HDR</strong></p></td><td  ><p>Dolby Vision, HDR10, HLG</p></td><td  ><p>Dolby Vision, HDR10+, HDR10, HLG</p></td></tr><tr><td class="firstcol " ><p><strong>Smart TV software</strong></p></td><td  ><p>Google TV</p></td><td  ><p>Fire TV</p></td></tr><tr><td class="firstcol " ><p><strong>ATSC 3.0 support?</strong></p></td><td  ><p>No</p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>XR Processor</p></td><td  ><p>Hi-View AI Engine</p></td></tr></tbody></table></div><p>Despite its entry-level status, the Bravia 3 II's spec sheet is one of the best of I've seen on a Sony TV in recent years, and it all starts with its full slate of HDMI 2.1-compatible inputs. Until now, it's been tricky to track down a Sony TV with more than two high-bandwidth ports.</p><p>Whichever HDMI port you pick, it's ready to make the most of your connected device. All four support 4K games at 120Hz — the top 4K spec on current-generation consoles.</p><p>The Hisense U6SF Pro, meanwhile, supports 4K content up to 144Hz, so it's slightly more accommodating for people who plan on linking up a gaming PC. However, only two of its HDMI ports support this functionality, so the Bravia 3 II's port selection is better equipped for a dedicated gamers.</p><p>Another key advantage of the Bravia 3 II is its wider array of size options. It's available in both 43- and 50-inch screen sizes, which is relatively hard to come by in its class.</p><p>The U6SF Pro's HDR10+ support is nice to have, but the added size options in the Bravia 3 II lineup and its universal HDMI 2.1 support put it over the top.</p><p><em><strong>Winner: </strong></em><em>Sony Bravia 3 II</em></p><h2 id="sony-bravia-3-ii-vs-hisense-u6sf-pro-design">Sony Bravia 3 II vs Hisense U6SF Pro: Design</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.15%;"><img id="uBGFHiMMLgDXDsogk9jYe5" name="Sony-Bravia-3-II--19" alt="The back of a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/uBGFHiMMLgDXDsogk9jYe5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Neither of these budget-friendly TVs are dressed as dashingly as higher-end models. However, given their place in each brand's respective lineup, they <em>could</em> look and feel a lot worse than they do.</p><p>The Sony Bravia 3 II (pictured above) leverages a sturdy panel that looks about as utilitarian as a TV gets. That said, I appreciate the minimalist approach, as the Bravia 3 II is able to slot right into any living room without looking aesthetically out of place.</p><p>At close to three inches thick, the Bravia 3 II's panel is a bit of a <em>chonkster. </em>If you're expecting something slim, you might want to take a look at it in person before plunking down your credit card. In any event, the U6SF Pro isn't much thinner.</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="zx6r6rPCcxxc86JB4XmKfn" name="Hisense-65U6SF-Pro-2026--18" alt="The back of a Hisense U6SF Pro TV" src="https://cdn.mos.cms.futurecdn.net/zx6r6rPCcxxc86JB4XmKfn-1920-80.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>Take a trip around the back of the U6SF Pro (seen above) and you'll notice that its panel has a bit more business going on — lines, indents, things of that nature. Most notably is its built-in subwoofer, which according to my colleague Dylan Haas, does a pretty decent job of filling out the TV's soundscape.</p><p>Both TV's screens sit somewhat low to the ground, but the Bravia 3 II offers a bit more soundbar clearance. In addition, I have to shout out the remote control included with the Bravia 3 II, which is much more pleasant to hold and use than the clicker that comes with the U6SF Pro.</p><p>But when sizing up both sets, their design chops are pretty much a wash. They'll look fine in all rooms and don't feel cheap to the touch.</p><p><em><strong>Winner:</strong></em><em>  Draw</em></p><h2 id="sony-bravia-3-ii-vs-hisense-u6sf-pro-performance">Sony Bravia 3 II vs Hisense U6SF Pro: Performance</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="ZUsGHhLnyFsvwLyudVXtUo" name="Hisense-65U6SF-Pro-2026--12" alt="Hisense U6SF Pro TV on a desk" src="https://cdn.mos.cms.futurecdn.net/ZUsGHhLnyFsvwLyudVXtUo-1920-80.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>Before we dig deeper into how these TVs look, let's compare their test results. Both TVs were in their most-accurate picture modes when these tests were taken. (For the Bravia 3, this is Professional mode. For the U6SF Pro, it's Filmmaker mode.)</p><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>Sony Bravia 3 II</strong></p></th><th  ><p><strong>Hisense U6SF Pro</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>SDR Brightness (10%, in nits)    </strong></p></td><td  ><p>352</p></td><td  ><p>358</p></td></tr><tr><td class="firstcol " ><p><strong>Delta-E (lower is better)    </strong></p></td><td  ><p>2.1</p></td><td  ><p>1.6</p></td></tr><tr><td class="firstcol " ><p><strong>HDR Brightness (10%, in nits)    </strong></p></td><td  ><p>520</p></td><td  ><p>1,265</p></td></tr><tr><td class="firstcol " ><p><strong>UHDA-P3 Gamut Coverage    </strong></p></td><td  ><p>94.62%</p></td><td  ><p>96.1%</p></td></tr><tr><td class="firstcol " ><p><strong>Rec.2020 Gamut Coverage    </strong></p></td><td  ><p>74.06% </p></td><td  ><p>75.12%</p></td></tr><tr><td class="firstcol " ><p><strong>Input latency (milliseconds)   </strong></p></td><td  ><p>9.8</p></td><td  ><p>9.3</p></td></tr></tbody></table></div><p>The U6SF Pro's primary advantage over the Bravia 3 II is its display hardware. It taps an array of Mini-LEDs that can be brightened and dimmed across multiple zones, while Sony's entry-level set is limited to standard, direct-LED backlighting with no local dimming at all.</p><p>The result — as is illustrated in the chart above — is that the U6SF Pro features a <em>much</em> brighter presentation. This is especially true for HDR content, where Mini-LEDs really shine.</p><p>Additionally, the U6SF Pro's smaller-sized LEDs and local dimming capabilities allow for tighter contrast control. Black levels are deeper than they are on the Bravia 3 II during certain content, which contributes to a more cinematic picture overall.</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.15%;"><img id="z9qpiWtks64ixxEWPG5Mb5" name="Sony-Bravia-3-II--7" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/z9qpiWtks64ixxEWPG5Mb5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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><div><blockquote><p>Our eyes have evolved to notice contrast above almost everything else.</p></blockquote></div><p>To its credit, the Bravia 3 II doesn't sport a bad-looking picture. True to Sony's reputation, the Bravia 3 II delivers a dialed-in presentation in its most-accurate picture mode. It handles the finer aspects of picture quality (like motion handling and upscaling) better than most entry-level sets.</p><p>But while Hisense's picture processing isn't as impressive as Sony's, our eyes have evolved to notice contrast above almost everything else. That the U6SF Pro is about twice as bright as the Bravia 3 II is what makes it the better choice for HDR content, bright-room viewing and with the lights off.</p><p>Colors look brighter on the U6SF Pro, too, as a result of this added luminance. When you factor this alongside the Bravia 3 II's limited contrast, you're left with a picture that runs the risk of looking flat in certain lighting conditions.</p><p><em><strong>Winner:</strong></em><em> Hisense U6SF Pro</em></p><h2 id="sony-bravia-3-ii-vs-hisense-u6sf-pro-smart-platform-and-features">Sony Bravia 3 II vs Hisense U6SF Pro: Smart platform and features</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.15%;"><img id="DypMFjKQD8twXfKohCrNi5" name="Sony-Bravia-3-II--10" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/DypMFjKQD8twXfKohCrNi5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Like all Bravia TVs, the Bravia 3 II is built around the Google TV smart platform. It's not my first choice when it comes to streaming software, but it's good enough to serve as a your daily viewing backdrop.</p><p>The Google TV interface is, for the most part, laid out in a sensible way, and while plenty of ads grace the home screen, it's not difficult to prioritize the apps and shows you're most likely to engage with. Google TV is also connected to a huge library of downloadable apps, so you'll rarely find yourself wondering why a piece of software isn't supported.</p><p>When it comes to bells and whistles, there's a lot to enjoy. The Bravia 3 II offers gamers Variable Refresh Rate (VRR) and Auto Low Latency Mode (ALLM), as well as the aforementioned quartet of HDMI 2.1 inputs. IMAX Enhanced mode is along for the ride, too, as well as all of the usual Google TV enhancements (like Chromecast functionality).</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="WyyAzYmMYzEc974dXHiZzn" name="Hisense-65U6SF-Pro-2026--14" alt="Hisense U6SF Pro TV on a desk" src="https://cdn.mos.cms.futurecdn.net/WyyAzYmMYzEc974dXHiZzn-1920-80.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>Unlike most mainline Hisense TVs in 2026, the U6SF Pro <em>isn't</em> running Google TV. Instead, it's built around Amazon's smart platform, Fire TV.</p><p>While Fire TV has seen some improvements in recent years, I still find the overall experience to be rather frustrating. At times it can be slow to boot up apps, and its user interface isn't as streamlined as that of Roku and Google TV. Unsurprisingly, folks who already use a number of Alexa-enabled devices in their home stand to make the most out of Fire TV.</p><p>Similar to the Bravia 3 II, the U6SF Pro also supports VRR, ALLM and IMAX Enhanced mode. It also advertises AMD FreeSync Premium Pro. The U6SF Pro <em>would be</em> a slightly better gaming TV than the Bravia 3 II if it offered as many HDMI 2.1-enabled ports.</p><p><em><strong>Winner:  </strong></em><em>Sony Bravia 3 II</em></p><h2 id="sony-bravia-3-ii-vs-hisense-u6sf-pro-verdict">Sony Bravia 3 II vs Hisense U6SF Pro: Verdict</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:42.85%;"><img id="gMvyKdpRaHv6kzjtCN4K8" name="Hisense-65U6SF-Pro-2026--6-HERO" alt="Hisense U6SF Pro TV on a desk" src="https://cdn.mos.cms.futurecdn.net/gMvyKdpRaHv6kzjtCN4K8-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="857" 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><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p>Sony Bravia 3 II</p></th><th  ><p>Hisense U6SF Pro</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Specs (25)</strong></p></td><td  ><p>23</p></td><td  ><p>22</p></td></tr><tr><td class="firstcol " ><p><strong>Design (25)</strong></p></td><td  ><p>21</p></td><td  ><p>21</p></td></tr><tr><td class="firstcol " ><p><strong>Performance (25)</strong></p></td><td  ><p>18</p></td><td  ><p>22</p></td></tr><tr><td class="firstcol " ><p><strong>Features (25)</strong></p></td><td  ><p>23</p></td><td  ><p>22</p></td></tr><tr><td class="firstcol " ><p><strong>Total Score (100)</strong></p></td><td  ><p><strong>85</strong></p></td><td  ><p><strong>87</strong></p></td></tr></tbody></table></div><p>The U6SF Pro is a safer pick for a wider variety of viewers than the Bravia 3 II. The reason for that is simple: Its picture is about twice as bright.</p><p>Thanks to its impressive picture processing, Google TV and the inclusion of four HDMI 2.1 inputs, an argument could easily be made that the Bravia 3 II is the best direct-LED-based TV on the market. It's certainly a fantastic gaming TV for the price, at least.</p><p>But the U6SF Pro's Mini-LED backlighting and local dimming are more impactful on a day-to-day basis than the Bravia 3's extra pair of high-bandwidth inputs, and the same goes for Google TV. The ideal Bravia 3 II-owner is someone who owns several consoles, appreciates the finer aspects of picture quality and does most of their viewing during the day (in a room that isn't <em>too</em> bright). That's a pretty narrow slice.</p><p>And we haven't even talked about pricing yet. The <a href="https://www.bestbuy.com/product/hisense-65-class-u6-pro-miniled-qled-uhd-4k-hdr-glare-free-smart-fire-tv-with-alexa-2026/J3Z9Z42WZZ" target="_blank" rel="nofollow">65-inch Hisense U6SF Pro is $699 at Best Buy</a> at the time of publishing — it's not the cheapest set, but you're getting a lot for that price.</p><p>Meanwhile, the <a href="https://www.bestbuy.com/product/sony-65-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-and-anti-glare-screen-2026/J7XSRH54PY" target="_blank" rel="nofollow">65-inch Bravia 3 II is currently $899 at Best Buy</a>. Unless you're absolutely certain that the Bravia 3 II's performance profile is a good fit for your living room, I recommend taking a closer look at the set with Mini-LED backlighting.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-WQA6zW"></div>                            </div>                            <script src="https://kwizly.com/embed/WQA6zW.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/tvs/4k-tvs/sony-bravia-3-ii-review">Sony Bravia 3 II review</a></li><li><a href="https://www.tomsguide.com/tvs/qled-tvs/hisense-u6sf-pro-review">Hisense U6SF Pro review</a></li><li><a href="https://www.tomsguide.com/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up">Best TV Brands 2026: We just graded them on price, performance and more — here’s how their report cards stack up</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Yes, LG smart TVs know more about you than you think — but so do other TV brands ]]></title>
                                                                                                <dc:content><![CDATA[ <p>LG — makers of some of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> on the market — is facing scrutiny after an investigative report revealed potential privacy vulnerabilities associated with its smart TVs. Specifically, the investigation purports that LG is aware that its TV users are being tracked to the degree that they are, but that in most cases, the user doesn’t.</p><p>For LG TV-owners, these developments could be cause for concern. Despite the seemingly endless spool of user agreements that unfurls during the set-up process, the extent to which a TV can track a person’s actions (and where that data ends up) remains eerily inscrutable.</p><p>But while LG is taking its turn under the microscope, every other TV brand is conveniently side-stepping the latest round of scrutiny. In 2026, every smart TV on the market is gathering a ton of data about who we are, what we watch and how likely we are to buy stuff — and there’s currently no incentive for them to stop.</p><h2 id="what-s-going-on-with-lg-smart-tvs">What’s going on with LG smart TVs?</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="uitP29WyfaomHeLZ2RpQf9" name="TG_LG-G5-TV-18" alt="The LG G5 OLED at Tom's Guide's Labs." src="https://cdn.mos.cms.futurecdn.net/uitP29WyfaomHeLZ2RpQf9-1920-80.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>Let’s start with some context. According to a recent investigation by YouTube channel <a href="https://www.youtube.com/watch?v=6IFVTcM28KA" target="_blank" rel="nofollow">Gamers Nexus</a>, LG smart TVs continuously scan and gather information about devices connected to a user’s home Wi-Fi network.</p><p>The <a href="https://www.tomsguide.com/tvs/your-lg-tv-could-be-tracking-devices-on-your-home-wi-fi-heres-how-to-stop-it">LG smart TV news</a> gets even more concerning, though, as Gamers Nexus also discovered that select models record audio via microphones in the TV and remote control — even while the TV is disconnected from the internet. These audio files are transcribed, stored and can be sent to LG when the TV connects to the internet.</p><p>We’ve reached out to LG for more information on these matters, but based on comments provided to outlets like <a href="https://arstechnica.com/gadgets/2026/09/lg-tv-shown-capable-of-tracking-user-activity-even-when-offline/?utm_source=flipboard&utm_content=user/ArsTechnica" target="_blank" rel="nofollow">Ars Technica</a> and <a href="https://www.tomshardware.com/tech-industry/big-tech/lg-strongly-denies-tv-security-claims-says-tracking-and-snooping-concerns-not-true-online-investigation-claims-216-000-000-spy-tvs-record-audio" target="_blank" rel="nofollow">Tom’s Hardware</a>, the stated function of LG TVs’ network analyzation function is to recognize and enable compatible devices that can interface with the TV.</p><div><blockquote><p>Select models record audio via microphones in the TV and remote control — even while the TV is disconnected from the internet. </p></blockquote></div><p>Additionally, LG claims that its TVs’ always-on listening function is limited to situations in which this feature has been deliberately enabled by the user, either by via a button on the remote or when the TV detects the “wake” words (like “Hi, LG”). According to LG, transcribed text is locally processed and deleted — not stored.</p><p>Like many tech brands that deal heavily in the gathering and mapping of user data, LG insists that its practices are above board. But critics (myself included) maintain that the process in which data is collected and catalogued is too opaque for most people to understand, and that allowing companies increasing access to our private lives introduces certain risks.</p><p>LG happens to be the brand catching the most flak right now, but it’s far from the only actor engaging in these dealings. In fact, I can’t think of a single TV-maker that isn’t.</p><h2 id="lg-is-not-alone">LG is not alone</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:2752px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2z7VjjBDu7CfpZvJzVmAfY" name="Roku oled" alt="The Roku Pro Series OLED TV in a living room." src="https://cdn.mos.cms.futurecdn.net/2z7VjjBDu7CfpZvJzVmAfY-1920-80.jpg" mos="" align="middle" fullscreen="" width="2752" height="1548" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Roku)</span></figcaption></figure><p>The worldwide leader in OLED TVs isn’t the only brand whose smart TVs are built around data-collection. Nearly every major TV-maker in the world is getting in on the action and has been for years.</p><p>As TV-enjoyers, we awkwardly reap some of the benefits of this system. One of the biggest reasons why TVs have gotten better and cheaper is because brands are able to offset costs by collecting and selling user data.</p><p>But make no mistake: We are nowhere near the primary benefactors here. These companies study who we are and what we watch, map our habits and find ways to maximize that information.</p><p>In addition to LG, Hisense, Samsung, Sony and TCL have faced criticism for their use of Automatic Content Recognition (ACR), a built-in function that tracks what’s on screen, gathers screenshots and shares the data with the brand associated with the TV or its software. All of that on-screen information can then be used as a roadmap for trying to sell stuff back to the user.</p><div><blockquote><p>Almost every smart TV on shelves right now is equipped with at least one microphone. </p></blockquote></div><p>Almost every smart TV on shelves right now is equipped with at least one microphone. And, while there are ways to disable these mics, the amount of software entanglement that exists between voice-activated smart home devices, TVs and smartphones provides plenty of incentive for people to smash that “Agree” button at the bottom of a wall of complicated legalese without much of a thought.</p><p>The frenzied, capitalistic advent of AI-based TV features — many of which offer little tangible benefit but are nevertheless the centerpiece of a company’s marketing pitch — all but ensures that the relationship between us and our data is about to get even more opaque.</p><p>Smart TVs have made streaming more accessible and cheaper than ever, but along the way, there’s been a slow-creep of access granted to the companies we invite into our living rooms.</p><h2 id="how-to-protect-your-smart-tv-privacy">How to protect your smart TV privacy</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="uK8b5fuFZgu6v85a4vtH4J" name="smart tv.shutterstock_2381270767" alt="smart tv with remotes" src="https://cdn.mos.cms.futurecdn.net/uK8b5fuFZgu6v85a4vtH4J-1920-80.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>There’s no way around it: The <a href="https://www.tomsguide.com/tvs/heres-why-its-hard-to-find-dumb-tv-in-2026-and-why-you-shouldnt-buy-one-anyway">best “Dumb” TVs</a> don’t really exist, and if you’re buying a smart TV, there’s a certain amount of privacy you’ll have to give up in order to use it. There <em>are</em>, however, ways to nibble around the edges of the situation.</p><p>We recently published a guide on <a href="https://www.tomsguide.com/tvs/your-lg-tv-could-be-tracking-devices-on-your-home-wi-fi-heres-how-to-stop-it">how to stop an LG TV from listening</a>, but it wasn’t our first foray into the subject. We’ve also published a guide on <a href="https://www.tomsguide.com/tvs/your-tv-is-watching-you-how-to-turn-off-data-collection-on-lg-samsung-roku-and-more">how to turn off TV data collection</a> for several popular brands including Samsung, Roku, and sets built around Google TV and Amazon Fire TV.</p><p>If you’re unclear whether or not your TV is tapping an on-board listening device, we’ve outlined instructions on <a href="https://www.tomsguide.com/tvs/yes-your-tv-probably-has-a-microphone-in-it-heres-how-to-turn-it-off">how to disable a TV microphone</a>. The exact manner in which these on-board microphones are used depends on the brand associated with the TV and/or its software, but I highly recommend familiarizing yourself with your own set’s functionality.</p><p>Until something powerful compels TV companies to disclose more information about their business practices, we — the viewers — need to take it upon ourselves to understand what’s at stake when it comes to our personal data and to draw our own lines in the sand for what we deem acceptable and unacceptable. LG is the latest brand at the center of controversy, but it surely won’t be the last.</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/tvs/your-lg-tv-could-be-tracking-devices-on-your-home-wi-fi-heres-how-to-stop-it">Your LG TV could be tracking devices on your home Wi-Fi — here’s how to stop it</a></li><li><a href="https://www.tomsguide.com/tvs/yes-your-tv-probably-has-a-microphone-in-it-heres-how-to-turn-it-off">Yes, your TV probably has a microphone in it — here's how to turn it off</a></li><li><a href="https://www.tomsguide.com/tvs/heres-why-its-hard-to-find-dumb-tv-in-2026-and-why-you-shouldnt-buy-one-anyway">Here's why it's hard to find 'dumb TV' in 2026 — and why you shouldn't buy one anyway</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/yes-lg-smart-tvs-know-more-about-you-than-you-think-but-so-do-other-tv-brands</link>
                                                                            <description>
                            <![CDATA[ LG is far from the only brand that has serious questions to answer concerning smart TV privacy. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">uHvH5tvmXPNZs2oy6cC9DL</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/sdYXGmHMCP7u9WFkt43kRi-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 12 Sep 2026 09:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ michael.desjardin@futurenet.com (Michael Desjardin) ]]></author>                    <dc:creator><![CDATA[ Michael Desjardin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/esytxRqW9GNVZNVCf7BFyV-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael Desjardin is a Senior Editor for TVs at Tom&#039;s Guide. He&#039;s been testing and tinkering with TVs professionally for over a decade, previously for Reviewed and USA Today. Michael studied media production and screenwriting at Emerson College. He loves cooking, zoning out to ambient music, and getting way too invested in the Red Sox. He considers himself living proof that TV doesn&#039;t necessarily rot your brain.&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/sdYXGmHMCP7u9WFkt43kRi-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[LG C6 OLED TV shown on a wood table]]></media:description>                                                            <media:text><![CDATA[LG C6 OLED TV shown on a wood table]]></media:text>
                                <media:title type="plain"><![CDATA[LG C6 OLED TV shown on a wood table]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/sdYXGmHMCP7u9WFkt43kRi-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>LG — makers of some of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> on the market — is facing scrutiny after an investigative report revealed potential privacy vulnerabilities associated with its smart TVs. Specifically, the investigation purports that LG is aware that its TV users are being tracked to the degree that they are, but that in most cases, the user doesn’t.</p><p>For LG TV-owners, these developments could be cause for concern. Despite the seemingly endless spool of user agreements that unfurls during the set-up process, the extent to which a TV can track a person’s actions (and where that data ends up) remains eerily inscrutable.</p><p>But while LG is taking its turn under the microscope, every other TV brand is conveniently side-stepping the latest round of scrutiny. In 2026, every smart TV on the market is gathering a ton of data about who we are, what we watch and how likely we are to buy stuff — and there’s currently no incentive for them to stop.</p><h2 id="what-s-going-on-with-lg-smart-tvs">What’s going on with LG smart TVs?</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="uitP29WyfaomHeLZ2RpQf9" name="TG_LG-G5-TV-18" alt="The LG G5 OLED at Tom's Guide's Labs." src="https://cdn.mos.cms.futurecdn.net/uitP29WyfaomHeLZ2RpQf9-1920-80.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>Let’s start with some context. According to a recent investigation by YouTube channel <a href="https://www.youtube.com/watch?v=6IFVTcM28KA" target="_blank" rel="nofollow">Gamers Nexus</a>, LG smart TVs continuously scan and gather information about devices connected to a user’s home Wi-Fi network.</p><p>The <a href="https://www.tomsguide.com/tvs/your-lg-tv-could-be-tracking-devices-on-your-home-wi-fi-heres-how-to-stop-it">LG smart TV news</a> gets even more concerning, though, as Gamers Nexus also discovered that select models record audio via microphones in the TV and remote control — even while the TV is disconnected from the internet. These audio files are transcribed, stored and can be sent to LG when the TV connects to the internet.</p><p>We’ve reached out to LG for more information on these matters, but based on comments provided to outlets like <a href="https://arstechnica.com/gadgets/2026/09/lg-tv-shown-capable-of-tracking-user-activity-even-when-offline/?utm_source=flipboard&utm_content=user/ArsTechnica" target="_blank" rel="nofollow">Ars Technica</a> and <a href="https://www.tomshardware.com/tech-industry/big-tech/lg-strongly-denies-tv-security-claims-says-tracking-and-snooping-concerns-not-true-online-investigation-claims-216-000-000-spy-tvs-record-audio" target="_blank" rel="nofollow">Tom’s Hardware</a>, the stated function of LG TVs’ network analyzation function is to recognize and enable compatible devices that can interface with the TV.</p><div><blockquote><p>Select models record audio via microphones in the TV and remote control — even while the TV is disconnected from the internet. </p></blockquote></div><p>Additionally, LG claims that its TVs’ always-on listening function is limited to situations in which this feature has been deliberately enabled by the user, either by via a button on the remote or when the TV detects the “wake” words (like “Hi, LG”). According to LG, transcribed text is locally processed and deleted — not stored.</p><p>Like many tech brands that deal heavily in the gathering and mapping of user data, LG insists that its practices are above board. But critics (myself included) maintain that the process in which data is collected and catalogued is too opaque for most people to understand, and that allowing companies increasing access to our private lives introduces certain risks.</p><p>LG happens to be the brand catching the most flak right now, but it’s far from the only actor engaging in these dealings. In fact, I can’t think of a single TV-maker that isn’t.</p><h2 id="lg-is-not-alone">LG is not alone</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:2752px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2z7VjjBDu7CfpZvJzVmAfY" name="Roku oled" alt="The Roku Pro Series OLED TV in a living room." src="https://cdn.mos.cms.futurecdn.net/2z7VjjBDu7CfpZvJzVmAfY-1920-80.jpg" mos="" align="middle" fullscreen="" width="2752" height="1548" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Roku)</span></figcaption></figure><p>The worldwide leader in OLED TVs isn’t the only brand whose smart TVs are built around data-collection. Nearly every major TV-maker in the world is getting in on the action and has been for years.</p><p>As TV-enjoyers, we awkwardly reap some of the benefits of this system. One of the biggest reasons why TVs have gotten better and cheaper is because brands are able to offset costs by collecting and selling user data.</p><p>But make no mistake: We are nowhere near the primary benefactors here. These companies study who we are and what we watch, map our habits and find ways to maximize that information.</p><p>In addition to LG, Hisense, Samsung, Sony and TCL have faced criticism for their use of Automatic Content Recognition (ACR), a built-in function that tracks what’s on screen, gathers screenshots and shares the data with the brand associated with the TV or its software. All of that on-screen information can then be used as a roadmap for trying to sell stuff back to the user.</p><div><blockquote><p>Almost every smart TV on shelves right now is equipped with at least one microphone. </p></blockquote></div><p>Almost every smart TV on shelves right now is equipped with at least one microphone. And, while there are ways to disable these mics, the amount of software entanglement that exists between voice-activated smart home devices, TVs and smartphones provides plenty of incentive for people to smash that “Agree” button at the bottom of a wall of complicated legalese without much of a thought.</p><p>The frenzied, capitalistic advent of AI-based TV features — many of which offer little tangible benefit but are nevertheless the centerpiece of a company’s marketing pitch — all but ensures that the relationship between us and our data is about to get even more opaque.</p><p>Smart TVs have made streaming more accessible and cheaper than ever, but along the way, there’s been a slow-creep of access granted to the companies we invite into our living rooms.</p><h2 id="how-to-protect-your-smart-tv-privacy">How to protect your smart TV privacy</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="uK8b5fuFZgu6v85a4vtH4J" name="smart tv.shutterstock_2381270767" alt="smart tv with remotes" src="https://cdn.mos.cms.futurecdn.net/uK8b5fuFZgu6v85a4vtH4J-1920-80.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>There’s no way around it: The <a href="https://www.tomsguide.com/tvs/heres-why-its-hard-to-find-dumb-tv-in-2026-and-why-you-shouldnt-buy-one-anyway">best “Dumb” TVs</a> don’t really exist, and if you’re buying a smart TV, there’s a certain amount of privacy you’ll have to give up in order to use it. There <em>are</em>, however, ways to nibble around the edges of the situation.</p><p>We recently published a guide on <a href="https://www.tomsguide.com/tvs/your-lg-tv-could-be-tracking-devices-on-your-home-wi-fi-heres-how-to-stop-it">how to stop an LG TV from listening</a>, but it wasn’t our first foray into the subject. We’ve also published a guide on <a href="https://www.tomsguide.com/tvs/your-tv-is-watching-you-how-to-turn-off-data-collection-on-lg-samsung-roku-and-more">how to turn off TV data collection</a> for several popular brands including Samsung, Roku, and sets built around Google TV and Amazon Fire TV.</p><p>If you’re unclear whether or not your TV is tapping an on-board listening device, we’ve outlined instructions on <a href="https://www.tomsguide.com/tvs/yes-your-tv-probably-has-a-microphone-in-it-heres-how-to-turn-it-off">how to disable a TV microphone</a>. The exact manner in which these on-board microphones are used depends on the brand associated with the TV and/or its software, but I highly recommend familiarizing yourself with your own set’s functionality.</p><p>Until something powerful compels TV companies to disclose more information about their business practices, we — the viewers — need to take it upon ourselves to understand what’s at stake when it comes to our personal data and to draw our own lines in the sand for what we deem acceptable and unacceptable. LG is the latest brand at the center of controversy, but it surely won’t be the last.</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/tvs/your-lg-tv-could-be-tracking-devices-on-your-home-wi-fi-heres-how-to-stop-it">Your LG TV could be tracking devices on your home Wi-Fi — here’s how to stop it</a></li><li><a href="https://www.tomsguide.com/tvs/yes-your-tv-probably-has-a-microphone-in-it-heres-how-to-turn-it-off">Yes, your TV probably has a microphone in it — here's how to turn it off</a></li><li><a href="https://www.tomsguide.com/tvs/heres-why-its-hard-to-find-dumb-tv-in-2026-and-why-you-shouldnt-buy-one-anyway">Here's why it's hard to find 'dumb TV' in 2026 — and why you shouldn't buy one anyway</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I ran Counter-Strike at 600fps to test out Sony’s elite esports monitor — here’s my verdict ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Sony’s elite esports gaming monitor just got, well, even more elite. The Inzone M10S II succeeds (you guessed it) the original M10S — a monitor I know well. I attended the original M10S launch at Gamescom 2024, where I spoke with the Fnatic esports team, who were heavily involved in its design, and the Sony engineers who brought Fnatic’s requests to life.</p><p>The new model shares the same core objectives as its predecessor: to provide professional esports gamers, or those aspiring to be one, with everything they need. As such, the core formula remains largely unchanged: blazing refresh and response rates; an array of features tailored entirely to esports; all combined with Sony’s premium design.</p><p>This time, refresh rates have been boosted in QHD and FHD modes; there’s a new anti-glare coating and a brightness boost, at least on paper. So, is this the <a href="https://www.tomsguide.com/us/best-gaming-monitors,review-3114.html"><u>best gaming monitor</u></a> for elite players? Find out in my full Sony Inzone M10S II review.</p><h2 class="article-body__section" id="section-sony-inzone-m10s-ii-review-cheat-sheet"><span>Sony Inzone M10S II review: Cheat sheet</span></h2><ul><li><strong>What is it? </strong>An elite esports gaming monitor</li><li><strong>Who is it for? </strong>Pro esports gamers, or those aspiring to be</li><li><strong>What does it cost? </strong><a href="https://electronics.sony.com/tv-video/gaming-monitors/all-inzone-monitors/p/sdm27q102-b" target="_blank" rel="nofollow">$1,099</a> / <a href="https://www.amazon.co.uk/Sony-INZONE-M10S-Gaming-Monitor/dp/B0GVJFDTB3/" target="_blank" rel="nofollow">£1,199</a></li><li><strong>What do we like? </strong>Be-all, end-all gaming performance and features; premium and thoughtful design</li><li><strong>What don’t we like? </strong>Not the brightest compared to other OLED monitors; USB-A and -B ports only</li></ul><h2 class="article-body__section" id="section-sony-inzone-m10s-ii-review-specs"><span>Sony Inzone M10S II review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://electronics.sony.com/tv-video/gaming-monitors/all-inzone-monitors/p/sdm27q102-b" target="_blank" rel="nofollow">$1,099</a> / <a href="https://www.amazon.co.uk/Sony-INZONE-M10S-Gaming-Monitor/dp/B0GVJFDTB3/" target="_blank" rel="nofollow">£1,199</a></p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>27-inch</p></td></tr><tr><td class="firstcol " ><p><strong>Resolution</strong></p></td><td  ><p>2560 x 1440</p></td></tr><tr><td class="firstcol " ><p><strong>Aspect ratio</strong></p></td><td  ><p>16:9</p></td></tr><tr><td class="firstcol " ><p><strong>Refresh rate</strong></p></td><td  ><p>540Hz (QHD); 720Hz (HD)</p></td></tr><tr><td class="firstcol " ><p><strong>Response time</strong></p></td><td  ><p>0.02ms</p></td></tr><tr><td class="firstcol " ><p><strong>Panel type</strong></p></td><td  ><p>OLED</p></td></tr><tr><td class="firstcol " ><p><strong>Color depth</strong></p></td><td  ><p>10-bit</p></td></tr><tr><td class="firstcol " ><p><strong>Inputs</strong></p></td><td  ><p>DisplayPort 2.1; 2x HDMI 2.1; 1x USB Type-B upstream; 2x USB Type-A downstream; 1x USB Type-A (software update only); 3.5mm jack</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>23.8 x 19.8 x 7.8 inches (with stand)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>13.67 lbs (with stand)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sony-inzone-m10s-ii-review-the-ups"><span>Sony Inzone M10S II review: The ups</span></h2><p>The M10S II has one job: to be the ultimate gaming monitor for esports players. With its impressive specs and an extensive array of performance and quality-of-life features, it succeeds. And as a Sony product, it boasts premium looks and a high-quality build.</p><h2 id="ultra-fast-performance">Ultra-fast performance</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="dgELH7YWCoEkHwQJffwJaZ" name="Sony_M10S2_ 2.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/dgELH7YWCoEkHwQJffwJaZ-1920-80.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><div  class="fancy-box"><div class="fancy_box-title">Tom's Guide reviews gaming platform</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Supplied by: </strong><a data-analytics-id="inline-link" href="https://www.msi.com/" target="_blank" rel="nofollow"><strong>MSI</strong></a><strong> | Tom's Guide</strong><br><strong></strong><br><strong>CPU:</strong> <a data-analytics-id="inline-link" href="https://www.amazon.com/AMD-7700X-16-Thread-Unlocked-Processor/dp/B0BBHHT8LY/" target="_blank" rel="nofollow">AMD Ryzen 7 7700X</a><strong> </strong>|<strong> Graphics card:</strong> <a data-analytics-id="inline-link" href="https://www.msi.com/Graphics-Card/GeForce-RTX-5070-Ti-16G-VANGUARD-SOC" target="_blank" rel="nofollow">MSI RTX 5070 Ti 16GB Vanguard SOC</a><strong> </strong>|<strong> Motherboard: </strong><a data-analytics-id="inline-link" href="https://www.msi.com/Motherboard/B850-GAMING-PLUS-WIFI" target="_blank" rel="nofollow">MSI B850E Gaming Plus WiFi</a><strong> </strong>|<strong> RAM: </strong><a data-analytics-id="inline-link" href="https://www.newegg.com/kingston-technology-corp-fury-renegade-32gb-ddr5-8000-cas-latency-cl38-desktop-memory-silver-black/p/N82E16820242829" target="_blank" rel="nofollow">Kingston Fury Renegade DDR5 32GB</a><strong> </strong>|<strong> Cooler:</strong> <a data-analytics-id="inline-link" href="https://www.amazon.com/Noctua-NH-U12S-chromax-Black-Single-Tower-Cooler/dp/B07Y88BNYZ" target="_blank">Noctua NH-U12S</a><strong> </strong>|<strong> PSU:</strong> <a data-analytics-id="inline-link" href="https://www.msi.com/Power-Supply/MEG-Ai1300P-PCIE5" target="_blank" rel="nofollow">MSI MEG Ai1300P PCIE5</a><strong> </strong>|<strong> Case: </strong><a data-analytics-id="inline-link" href="https://www.msi.com/PC-Case/MPG-GUNGNIR-110R" target="_blank" rel="nofollow">MSI MPG GUNGNIR 110R</a></p></div></div><p>The M10S II packs so many gaming features that I’ll have to break them up. First, I’ll cover the hardware side of things. And <em>that</em> refresh rate! With 540Hz in QHD mode and up to 720Hz when dropping to 720p (HD), this monitor is absolutely rapid, relaying enemy movement as accurately as possible, with no motion blur or lag.</p><p>With an ultra low response time of 0.02ms gtg (gray to gray, which means how fast the pixels can change color), plus Sony’s Motion Blur Reduction, ghosting is effectively removed. There’s also G-Sync to remove tearing, especially in situations with rapidly changing frame rates. All this means the enemy you’re shooting at is more likely to be exactly where you’re seeing it, and you aren’t shooting its ghost trail or some other artefact.</p><p>I’m not an esports pro, of course, but playing Counter-Strike 2 at 600fps was a smooth experience — though not particularly enjoyable at 720p. Our TG testing rig couldn’t hit the maximum 720fps, even with Counter-Strike set to low and running at 720p, but topping out at 600fps was still pretty cool. At 1440p with Counter-Strike on high settings, our testing rig still hit around 400fps.</p><p>The original M10S looks like a tortoise in comparison, topping out at <em><strong>only</strong></em> 480Hz and a <em><strong>mere</strong></em> 0.03ms response time. Our favorite high-refresh-rate monitor (up to now), the <a href="https://www.tomsguide.com/computing/monitors/msi-mag-272qp-qd-oled-x50-review"><u>MSI MAG 272QP QD-OLED X50</u></a> ($749), manages <em><strong>just</strong></em> 500Hz.</p><h2 id="plethora-of-gaming-features">Plethora of gaming features</h2><p>The M10S II also offers a comprehensive array of gaming features designed to suit elite gamers — like the Fnatic team. There’s a 24.5-inch tournament crop for competition practice, and two FPS modes, FPS Pro (which simulates an LCD) and FPS Pro+, which adjust contrast ratios to make enemy detection easier, especially in dimly lit game scenarios. I especially enjoyed using FPS Pro when playing Hell Let Loose, particularly on darker nighttime maps, as it lifted the blacks a little to help me spot enemies hiding in bushes.</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="S4SAwUmha8JfVBRu7TriZa" name="Sony_M10S2_ 5.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/S4SAwUmha8JfVBRu7TriZa-1920-80.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>Those aren’t the only game modes, though, as there’s a specific MOBA/RTS mode, as well as a general game mode for casual play.</p><p>There’s also an on-screen crosshair with a variety of crosshair types to choose from. I found this really handy in Counter-Strike 2, where many guns have no ADS mode and the in-game crosshair expands and contracts with movement and movement penalties. It was also super useful in Hell Let Loose, as it allowed me to more accurately fire from the hip and score reflex kills on enemies popping up from bushes or around corners, which I might otherwise have missed.</p><p>On that note, there’s also a black equalizer mode to brighten shadows without affecting lighter areas of the screen. This is handy if you’re already in a game-specific mode and only want to boost shadows, or if affecting highlights would hamper your performance.</p><h2 id="plus-some-useful-qol-features">Plus some useful QoL features</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="6ZRAudYR7PuyXrwbVscXiZ" name="Sony_M10S2_ 3.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/6ZRAudYR7PuyXrwbVscXiZ-1920-80.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 also appreciate some of the quality-of-life (QoL) features found in the M10S range. The M10S II, like its predecessor, is designed with setup flexibility as a key consideration, and plenty of attention has been paid to the details.</p><p>In particular, there’s the stand. The M10S II features the same cantilevered stand as its predecessor. I actually spoke to the engineer who designed this stand at the original M10S press briefing — the stand was his passion project because he was trying to solve an issue he had with monitors. The cantilever channels all the weight into the center for perfect balance, allowing Sony to keep the base small.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/EmMsEVzXmWDZwR9XjPvFYF-1920-80.jpg" alt="A keyboard about to be slid over the M10S II's base stand" /><figcaption>Before!<small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/xD4knPrxjzJpq95fLuAYTF-1920-80.jpg" alt="A keyboard over the M10S II's base stand" /><figcaption>After!<small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/AmxfvLVC93t7jNrUE2ZFaF-1920-80.jpg" alt="The Sony M10S II's cable tidy" /><figcaption>And the cable tidy doing its job admirably.<small role="credit">Future</small></figcaption></figure></figure><p>The M10S II is therefore much easier to position than many other monitors. The M10S II replaced my <a href="https://www.tomsguide.com/computing/monitors/lenovo-legion-r27qe-gen-2-review"><u>Lenovo Legion R27qe Gen 2</u></a> (£199, U.K. only), with its enormous tripod stand — with the Sony, my desk felt so much more spacious and open, and for once I had room for other products I was testing. The base is also extremely thin, deliberately designed so you can slide your keyboard over it and keep your desk tidy when not in use. The arm swivels, so you can adjust the monitor's rotation without having to pick up and move the whole assembly. And there's a neat cable tidy to, well, keep your cables tidy!</p><p>I’m also a big fan of the anti-glare coating on the screen. I work with a window behind me in the office, along with overhead lighting. I was able to game and work on the M10S II for hours without eye strain or headaches.</p><h2 id="accurate-color-rendition">Accurate color rendition</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="s2sHA2gKHAZTa69V3Et8Ha" name="Sony_M10S2_ 4.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/s2sHA2gKHAZTa69V3Et8Ha-1920-80.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>While the M10S II isn’t a production monitor, it’s still important that the panel reproduces color accurately. Thankfully, the M10S II does! As you can see in our lab test results below, the M10S II achieved a Delta-E score of 0.26. This score measures how closely the on-screen color matches the color sent to the display by the computer, and the lower the better.</p><div ><table><thead><tr><th class="firstcol " ><p><strong>Product name</strong></p></th><th  ><p><strong>Panel type</strong></p></th><th  ><p><strong>Delta E</strong></p></th><th  ><p><strong>sRGB volume</strong></p></th><th  ><p><strong>DCI P3 volume</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Sony Inzone M10S II</strong></p></td><td  ><p>OLED</p></td><td  ><p>0.26</p></td><td  ><p>185.80%</p></td><td  ><p>131.60%</p></td></tr><tr><td class="firstcol " ><p><a href="https://www.tomsguide.com/computing/monitors/msi-mpg-272urx-qd-oled-monitor-review"><strong>MSI MPG 272URX QD-OLED</strong></a></p></td><td  ><p>OLED</p></td><td  ><p>0.15</p></td><td  ><p>131%</p></td><td  ><p>97%</p></td></tr><tr><td class="firstcol " ><p><a href="https://www.tomsguide.com/computing/monitors/lenovo-legion-pro-34wd-10-oled-review"><strong>Lenovo Legion Pro 34WD10</strong></a></p></td><td  ><p>OLED</p></td><td  ><p>0.08</p></td><td  ><p>156.40%</p></td><td  ><p>110.80%</p></td></tr><tr><td class="firstcol " ><p><a href="https://www.tomsguide.com/computing/monitors/alienware-aw2725q-gaming-monitor-review"><strong>Alienware AW2725Q</strong></a></p></td><td  ><p>QD-OLED</p></td><td  ><p>0.06</p></td><td  ><p>182.50%</p></td><td  ><p>129.30%</p></td></tr><tr><td class="firstcol " ><p><a href="https://www.tomsguide.com/computing/monitors/msi-mag-272qp-qd-oled-x50-review"><strong>MSI MAG 272 QP QD-OLED X50</strong></a></p></td><td  ><p>OLED</p></td><td  ><p>0.11</p></td><td  ><p>192.90%</p></td><td  ><p>136.70%</p></td></tr><tr><td class="firstcol " ><p><a href="https://www.tomsguide.com/computing/monitors/msi-mpg-341cqr-qd-oled-x36-review"><strong>MSI MPG 341CQR QD-OLED X36</strong></a></p></td><td  ><p>QD-OLED</p></td><td  ><p>0.15</p></td><td  ><p>166.30%</p></td><td  ><p>117.80%</p></td></tr></tbody></table></div><p>A 0.26 Delta-E isn’t as good as some of the other gaming monitors we’ve tested, such as the Alienware AW2525HM, MSI MAG 272 QP, or MSI MPG 341CQR QD-OLED X36. But it’s still fairly respectable, and it means the colors you’re seeing on screen are rendered accurately.</p><p>The M10S II also covers both the sRGB and DCI-P3 color gamuts. What does that mean? Games are mastered in sRGB for SDR and in DCI-P3 for HDR, so the M10S II can reproduce every unique color in every game you play.</p><p>Now, an ultra-wide gamut can cause oversaturation in those spaces, resulting in an unnatural, neon effect. So, more technically isn’t always better. But while colors appear very bold and vivid on the M10S II, they didn’t feel overdone. And besides, there are sRGB and Cinema (for DCI-P3) color modes if you want to ensure strict accuracy to those gamuts.</p><h2 id="sony-s-typically-premium-design">Sony’s typically premium design</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="TKK6c4T63tdmtZJU3kNfba" name="Sony_M10S2_ 8.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/TKK6c4T63tdmtZJU3kNfba-1920-80.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>Unsurprisingly, as this is a Sony product, the M10S II is built extremely well. The monitor looks and feels premium, with high-quality materials used throughout.</p><p>The rear of the display is finished in textured metal rather than plastic. The stand and base are also made from metal and feel extremely sturdy. It simply feels built to last, and there’s a three-year warranty to back it up anyway.</p><h2 id="arguably-good-value">Arguably good value</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="LarUzNpMtjrLRDAFWrQmuZ" name="Sony_M10S2_ 6.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/LarUzNpMtjrLRDAFWrQmuZ-1920-80.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>This might sound a little odd given that the M10S II costs <a href="https://electronics.sony.com/tv-video/gaming-monitors/all-inzone-monitors/p/sdm27q102-b" target="_blank" rel="nofollow"><u>$1,099</u></a> / <a href="https://www.amazon.co.uk/Sony-INZONE-M10S-Gaming-Monitor/dp/B0GVJFDTB3/" target="_blank" rel="nofollow"><u>£1,199</u></a>, but I’d argue it’s fairly good value. It’s not uncommon for gaming monitors to push well beyond the $1,000 mark. The <a href="https://www.tomsguide.com/computing/monitors/lenovo-legion-pro-34wd-10-oled-review"><u>Lenovo Legion Pro 34WD-10 OLED</u></a>, for example, launched at $1,099. The <a href="https://www.tomsguide.com/reviews/samsung-odyssey-oled-g9"><u>Samsung Odyssey OLED G9</u></a>, meanwhile, launched at $1,799.</p><p>I know: those are ultrawides, and their prices have dropped since launch. But what I mean here is that the M10S II isn’t priced beyond the pale. And yet it offers superb performance for elite gamers. If you’re one of those people, the M10S II could make strong financial and competitive sense.</p><h2 class="article-body__section" id="section-sony-inzone-m10s-ii-review-the-downs"><span>Sony Inzone M10S II review: The downs</span></h2><p>The Inzone M10S II doesn’t have many faults. However, it’s not the brightest OLED we’ve lab tested, and the USB ports on the underside are pretty dated.</p><h2 id="not-the-brightest">Not the brightest</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="HZwVkRiBJBLqzXttnbMSga" name="Sony_M10S2_ 7.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/HZwVkRiBJBLqzXttnbMSga-1920-80.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>We don’t like to rely on manufacturer claims, which are often best-case scenarios and can be a tad (to say the least) unrealistic. As such, as part of our lab testing process, we also measure peak SDR and HDR brightness.</p><div ><table><thead><tr><th class="firstcol " ><p><strong>Product name</strong></p></th><th  ><p><strong>Panel type</strong></p></th><th  ><p><strong>Nits (HDR, at 10%)</strong></p></th><th  ><p><strong>Nits (SDR)</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Sony Inzone M10S II</strong></p></td><td  ><p>OLED</p></td><td  ><p>146 </p></td><td  ><p>231</p></td></tr><tr><td class="firstcol " ><p><strong>MSI MPG 272URX QD-OLED</strong></p></td><td  ><p>OLED</p></td><td  ><p>449 </p></td><td  ><p>254</p></td></tr><tr><td class="firstcol " ><p><strong>Lenovo Legion Pro 34WD10</strong></p></td><td  ><p>OLED</p></td><td  ><p>443</p></td><td  ><p>265</p></td></tr><tr><td class="firstcol " ><p><strong>Alienware AW2725Q</strong></p></td><td  ><p>QD-OLED</p></td><td  ><p>334</p></td><td  ><p>250 </p></td></tr><tr><td class="firstcol " ><p><strong>MSI MAG 272 QP</strong></p></td><td  ><p>OLED</p></td><td  ><p>284 </p></td><td  ><p>289</p></td></tr><tr><td class="firstcol " ><p><strong>MSI MPG 341CQR QD-OLED X36</strong></p></td><td  ><p>QD-OLED</p></td><td  ><p>413</p></td><td  ><p>280</p></td></tr></tbody></table></div><p>As you can see in the test results, the M10S II isn’t as bright as other OLED gaming panels we’ve tested and is well below the (rather ambitious) 1,500-nit peak brightness claimed by Sony.</p><p>Still, when you think about it, this monitor’s primary purpose is to be used in a dark room. And besides, figures alone don’t always tell the full story, and I certainly wouldn’t let our measured peak brightness put you off buying this monitor. I’ve had no issues using it in bright environments, largely thanks to the anti-glare coating. And I never struggled with darker in-game scenarios, thanks to the different color modes and black point equalizer.</p><h2 id="dated-usb-ports">Dated USB ports</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="QMNtiWvm7wPDH9KhHvZa3a" name="Sony_M10S2_ 9.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/QMNtiWvm7wPDH9KhHvZa3a-1920-80.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 only other gripe with the M10S II is that it uses fairly dated ports, at least when it comes to USB (with HDMI 2.1 and DisplayPort 2.1, the video ports are just fine). There are two USB-A downstream ports and a USB-B upstream port. These are outdated, and it would’ve been nice to see at least a USB-C port or two.</p><p>Again, it’s important to remember use cases here. I’m not going to complain about the lack of a Thunderbolt port or KVM controller on a monitor that isn’t designed for productivity, but come on, it’s 2026… how can Sony fit a Type-B but not a single Type-C?</p><h2 class="article-body__section" id="section-sony-inzone-m10s-ii-review-verdict"><span>Sony Inzone M10S II 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="xdncXHJiq6rPWsCo2R3LFa" name="Sony_M10S2_.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/xdncXHJiq6rPWsCo2R3LFa-1920-80.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 you want the fastest and most feature-packed gaming monitor around, especially if you’re an elite competitive player or aspiring pro, I wholeheartedly recommend the Sony Inzone M10S II.</p><p>Its flaws are very few and fairly minor. What’s more, it not only delivers blazing-fast hardware performance but also a huge roster of genuinely useful gaming tools and thoughtful quality-of-life features! Then there’s Sony’s usual build quality and a fairly reasonable price tag, all things considered. I’m genuinely rather sad to be giving it back to Sony!</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/computing/monitors/sony-inzone-m10s-ii-review</link>
                                                                            <description>
                            <![CDATA[ The Sony Inzone M10S II is ultra fast, built beautifully and packs plenty of gaming features. Its flaws are few, although it isn't the brightest. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">JbAH7NarBmT6NbfUPdwVZR</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/nfhB5rkXZny2jcAYhL3Sga-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 11 Sep 2026 12:02:35 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Monitors]]></category>
                                                    <category><![CDATA[Gaming Peripherals]]></category>
                                                    <category><![CDATA[Computing Peripherals]]></category>
                                                    <category><![CDATA[Computing Hardware]]></category>
                                                    <category><![CDATA[Gaming]]></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-320-70.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/nfhB5rkXZny2jcAYhL3Sga-1920-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Sony Inzone M10S II against a blue gradient background]]></media:description>                                                            <media:text><![CDATA[The Sony Inzone M10S II against a blue gradient background]]></media:text>
                                <media:title type="plain"><![CDATA[The Sony Inzone M10S II against a blue gradient background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/nfhB5rkXZny2jcAYhL3Sga-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Sony’s elite esports gaming monitor just got, well, even more elite. The Inzone M10S II succeeds (you guessed it) the original M10S — a monitor I know well. I attended the original M10S launch at Gamescom 2024, where I spoke with the Fnatic esports team, who were heavily involved in its design, and the Sony engineers who brought Fnatic’s requests to life.</p><p>The new model shares the same core objectives as its predecessor: to provide professional esports gamers, or those aspiring to be one, with everything they need. As such, the core formula remains largely unchanged: blazing refresh and response rates; an array of features tailored entirely to esports; all combined with Sony’s premium design.</p><p>This time, refresh rates have been boosted in QHD and FHD modes; there’s a new anti-glare coating and a brightness boost, at least on paper. So, is this the <a href="https://www.tomsguide.com/us/best-gaming-monitors,review-3114.html"><u>best gaming monitor</u></a> for elite players? Find out in my full Sony Inzone M10S II review.</p><h2 class="article-body__section" id="section-sony-inzone-m10s-ii-review-cheat-sheet"><span>Sony Inzone M10S II review: Cheat sheet</span></h2><ul><li><strong>What is it? </strong>An elite esports gaming monitor</li><li><strong>Who is it for? </strong>Pro esports gamers, or those aspiring to be</li><li><strong>What does it cost? </strong><a href="https://electronics.sony.com/tv-video/gaming-monitors/all-inzone-monitors/p/sdm27q102-b" target="_blank" rel="nofollow">$1,099</a> / <a href="https://www.amazon.co.uk/Sony-INZONE-M10S-Gaming-Monitor/dp/B0GVJFDTB3/" target="_blank" rel="nofollow">£1,199</a></li><li><strong>What do we like? </strong>Be-all, end-all gaming performance and features; premium and thoughtful design</li><li><strong>What don’t we like? </strong>Not the brightest compared to other OLED monitors; USB-A and -B ports only</li></ul><h2 class="article-body__section" id="section-sony-inzone-m10s-ii-review-specs"><span>Sony Inzone M10S II review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://electronics.sony.com/tv-video/gaming-monitors/all-inzone-monitors/p/sdm27q102-b" target="_blank" rel="nofollow">$1,099</a> / <a href="https://www.amazon.co.uk/Sony-INZONE-M10S-Gaming-Monitor/dp/B0GVJFDTB3/" target="_blank" rel="nofollow">£1,199</a></p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>27-inch</p></td></tr><tr><td class="firstcol " ><p><strong>Resolution</strong></p></td><td  ><p>2560 x 1440</p></td></tr><tr><td class="firstcol " ><p><strong>Aspect ratio</strong></p></td><td  ><p>16:9</p></td></tr><tr><td class="firstcol " ><p><strong>Refresh rate</strong></p></td><td  ><p>540Hz (QHD); 720Hz (HD)</p></td></tr><tr><td class="firstcol " ><p><strong>Response time</strong></p></td><td  ><p>0.02ms</p></td></tr><tr><td class="firstcol " ><p><strong>Panel type</strong></p></td><td  ><p>OLED</p></td></tr><tr><td class="firstcol " ><p><strong>Color depth</strong></p></td><td  ><p>10-bit</p></td></tr><tr><td class="firstcol " ><p><strong>Inputs</strong></p></td><td  ><p>DisplayPort 2.1; 2x HDMI 2.1; 1x USB Type-B upstream; 2x USB Type-A downstream; 1x USB Type-A (software update only); 3.5mm jack</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>23.8 x 19.8 x 7.8 inches (with stand)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>13.67 lbs (with stand)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sony-inzone-m10s-ii-review-the-ups"><span>Sony Inzone M10S II review: The ups</span></h2><p>The M10S II has one job: to be the ultimate gaming monitor for esports players. With its impressive specs and an extensive array of performance and quality-of-life features, it succeeds. And as a Sony product, it boasts premium looks and a high-quality build.</p><h2 id="ultra-fast-performance">Ultra-fast performance</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="dgELH7YWCoEkHwQJffwJaZ" name="Sony_M10S2_ 2.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/dgELH7YWCoEkHwQJffwJaZ-1920-80.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><div  class="fancy-box"><div class="fancy_box-title">Tom's Guide reviews gaming platform</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Supplied by: </strong><a data-analytics-id="inline-link" href="https://www.msi.com/" target="_blank" rel="nofollow"><strong>MSI</strong></a><strong> | Tom's Guide</strong><br><strong></strong><br><strong>CPU:</strong> <a data-analytics-id="inline-link" href="https://www.amazon.com/AMD-7700X-16-Thread-Unlocked-Processor/dp/B0BBHHT8LY/" target="_blank" rel="nofollow">AMD Ryzen 7 7700X</a><strong> </strong>|<strong> Graphics card:</strong> <a data-analytics-id="inline-link" href="https://www.msi.com/Graphics-Card/GeForce-RTX-5070-Ti-16G-VANGUARD-SOC" target="_blank" rel="nofollow">MSI RTX 5070 Ti 16GB Vanguard SOC</a><strong> </strong>|<strong> Motherboard: </strong><a data-analytics-id="inline-link" href="https://www.msi.com/Motherboard/B850-GAMING-PLUS-WIFI" target="_blank" rel="nofollow">MSI B850E Gaming Plus WiFi</a><strong> </strong>|<strong> RAM: </strong><a data-analytics-id="inline-link" href="https://www.newegg.com/kingston-technology-corp-fury-renegade-32gb-ddr5-8000-cas-latency-cl38-desktop-memory-silver-black/p/N82E16820242829" target="_blank" rel="nofollow">Kingston Fury Renegade DDR5 32GB</a><strong> </strong>|<strong> Cooler:</strong> <a data-analytics-id="inline-link" href="https://www.amazon.com/Noctua-NH-U12S-chromax-Black-Single-Tower-Cooler/dp/B07Y88BNYZ" target="_blank">Noctua NH-U12S</a><strong> </strong>|<strong> PSU:</strong> <a data-analytics-id="inline-link" href="https://www.msi.com/Power-Supply/MEG-Ai1300P-PCIE5" target="_blank" rel="nofollow">MSI MEG Ai1300P PCIE5</a><strong> </strong>|<strong> Case: </strong><a data-analytics-id="inline-link" href="https://www.msi.com/PC-Case/MPG-GUNGNIR-110R" target="_blank" rel="nofollow">MSI MPG GUNGNIR 110R</a></p></div></div><p>The M10S II packs so many gaming features that I’ll have to break them up. First, I’ll cover the hardware side of things. And <em>that</em> refresh rate! With 540Hz in QHD mode and up to 720Hz when dropping to 720p (HD), this monitor is absolutely rapid, relaying enemy movement as accurately as possible, with no motion blur or lag.</p><p>With an ultra low response time of 0.02ms gtg (gray to gray, which means how fast the pixels can change color), plus Sony’s Motion Blur Reduction, ghosting is effectively removed. There’s also G-Sync to remove tearing, especially in situations with rapidly changing frame rates. All this means the enemy you’re shooting at is more likely to be exactly where you’re seeing it, and you aren’t shooting its ghost trail or some other artefact.</p><p>I’m not an esports pro, of course, but playing Counter-Strike 2 at 600fps was a smooth experience — though not particularly enjoyable at 720p. Our TG testing rig couldn’t hit the maximum 720fps, even with Counter-Strike set to low and running at 720p, but topping out at 600fps was still pretty cool. At 1440p with Counter-Strike on high settings, our testing rig still hit around 400fps.</p><p>The original M10S looks like a tortoise in comparison, topping out at <em><strong>only</strong></em> 480Hz and a <em><strong>mere</strong></em> 0.03ms response time. Our favorite high-refresh-rate monitor (up to now), the <a href="https://www.tomsguide.com/computing/monitors/msi-mag-272qp-qd-oled-x50-review"><u>MSI MAG 272QP QD-OLED X50</u></a> ($749), manages <em><strong>just</strong></em> 500Hz.</p><h2 id="plethora-of-gaming-features">Plethora of gaming features</h2><p>The M10S II also offers a comprehensive array of gaming features designed to suit elite gamers — like the Fnatic team. There’s a 24.5-inch tournament crop for competition practice, and two FPS modes, FPS Pro (which simulates an LCD) and FPS Pro+, which adjust contrast ratios to make enemy detection easier, especially in dimly lit game scenarios. I especially enjoyed using FPS Pro when playing Hell Let Loose, particularly on darker nighttime maps, as it lifted the blacks a little to help me spot enemies hiding in bushes.</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="S4SAwUmha8JfVBRu7TriZa" name="Sony_M10S2_ 5.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/S4SAwUmha8JfVBRu7TriZa-1920-80.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>Those aren’t the only game modes, though, as there’s a specific MOBA/RTS mode, as well as a general game mode for casual play.</p><p>There’s also an on-screen crosshair with a variety of crosshair types to choose from. I found this really handy in Counter-Strike 2, where many guns have no ADS mode and the in-game crosshair expands and contracts with movement and movement penalties. It was also super useful in Hell Let Loose, as it allowed me to more accurately fire from the hip and score reflex kills on enemies popping up from bushes or around corners, which I might otherwise have missed.</p><p>On that note, there’s also a black equalizer mode to brighten shadows without affecting lighter areas of the screen. This is handy if you’re already in a game-specific mode and only want to boost shadows, or if affecting highlights would hamper your performance.</p><h2 id="plus-some-useful-qol-features">Plus some useful QoL features</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="6ZRAudYR7PuyXrwbVscXiZ" name="Sony_M10S2_ 3.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/6ZRAudYR7PuyXrwbVscXiZ-1920-80.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 also appreciate some of the quality-of-life (QoL) features found in the M10S range. The M10S II, like its predecessor, is designed with setup flexibility as a key consideration, and plenty of attention has been paid to the details.</p><p>In particular, there’s the stand. The M10S II features the same cantilevered stand as its predecessor. I actually spoke to the engineer who designed this stand at the original M10S press briefing — the stand was his passion project because he was trying to solve an issue he had with monitors. The cantilever channels all the weight into the center for perfect balance, allowing Sony to keep the base small.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/EmMsEVzXmWDZwR9XjPvFYF-1920-80.jpg" alt="A keyboard about to be slid over the M10S II's base stand" /><figcaption>Before!<small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/xD4knPrxjzJpq95fLuAYTF-1920-80.jpg" alt="A keyboard over the M10S II's base stand" /><figcaption>After!<small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/AmxfvLVC93t7jNrUE2ZFaF-1920-80.jpg" alt="The Sony M10S II's cable tidy" /><figcaption>And the cable tidy doing its job admirably.<small role="credit">Future</small></figcaption></figure></figure><p>The M10S II is therefore much easier to position than many other monitors. The M10S II replaced my <a href="https://www.tomsguide.com/computing/monitors/lenovo-legion-r27qe-gen-2-review"><u>Lenovo Legion R27qe Gen 2</u></a> (£199, U.K. only), with its enormous tripod stand — with the Sony, my desk felt so much more spacious and open, and for once I had room for other products I was testing. The base is also extremely thin, deliberately designed so you can slide your keyboard over it and keep your desk tidy when not in use. The arm swivels, so you can adjust the monitor's rotation without having to pick up and move the whole assembly. And there's a neat cable tidy to, well, keep your cables tidy!</p><p>I’m also a big fan of the anti-glare coating on the screen. I work with a window behind me in the office, along with overhead lighting. I was able to game and work on the M10S II for hours without eye strain or headaches.</p><h2 id="accurate-color-rendition">Accurate color rendition</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="s2sHA2gKHAZTa69V3Et8Ha" name="Sony_M10S2_ 4.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/s2sHA2gKHAZTa69V3Et8Ha-1920-80.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>While the M10S II isn’t a production monitor, it’s still important that the panel reproduces color accurately. Thankfully, the M10S II does! As you can see in our lab test results below, the M10S II achieved a Delta-E score of 0.26. This score measures how closely the on-screen color matches the color sent to the display by the computer, and the lower the better.</p><div ><table><thead><tr><th class="firstcol " ><p><strong>Product name</strong></p></th><th  ><p><strong>Panel type</strong></p></th><th  ><p><strong>Delta E</strong></p></th><th  ><p><strong>sRGB volume</strong></p></th><th  ><p><strong>DCI P3 volume</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Sony Inzone M10S II</strong></p></td><td  ><p>OLED</p></td><td  ><p>0.26</p></td><td  ><p>185.80%</p></td><td  ><p>131.60%</p></td></tr><tr><td class="firstcol " ><p><a href="https://www.tomsguide.com/computing/monitors/msi-mpg-272urx-qd-oled-monitor-review"><strong>MSI MPG 272URX QD-OLED</strong></a></p></td><td  ><p>OLED</p></td><td  ><p>0.15</p></td><td  ><p>131%</p></td><td  ><p>97%</p></td></tr><tr><td class="firstcol " ><p><a href="https://www.tomsguide.com/computing/monitors/lenovo-legion-pro-34wd-10-oled-review"><strong>Lenovo Legion Pro 34WD10</strong></a></p></td><td  ><p>OLED</p></td><td  ><p>0.08</p></td><td  ><p>156.40%</p></td><td  ><p>110.80%</p></td></tr><tr><td class="firstcol " ><p><a href="https://www.tomsguide.com/computing/monitors/alienware-aw2725q-gaming-monitor-review"><strong>Alienware AW2725Q</strong></a></p></td><td  ><p>QD-OLED</p></td><td  ><p>0.06</p></td><td  ><p>182.50%</p></td><td  ><p>129.30%</p></td></tr><tr><td class="firstcol " ><p><a href="https://www.tomsguide.com/computing/monitors/msi-mag-272qp-qd-oled-x50-review"><strong>MSI MAG 272 QP QD-OLED X50</strong></a></p></td><td  ><p>OLED</p></td><td  ><p>0.11</p></td><td  ><p>192.90%</p></td><td  ><p>136.70%</p></td></tr><tr><td class="firstcol " ><p><a href="https://www.tomsguide.com/computing/monitors/msi-mpg-341cqr-qd-oled-x36-review"><strong>MSI MPG 341CQR QD-OLED X36</strong></a></p></td><td  ><p>QD-OLED</p></td><td  ><p>0.15</p></td><td  ><p>166.30%</p></td><td  ><p>117.80%</p></td></tr></tbody></table></div><p>A 0.26 Delta-E isn’t as good as some of the other gaming monitors we’ve tested, such as the Alienware AW2525HM, MSI MAG 272 QP, or MSI MPG 341CQR QD-OLED X36. But it’s still fairly respectable, and it means the colors you’re seeing on screen are rendered accurately.</p><p>The M10S II also covers both the sRGB and DCI-P3 color gamuts. What does that mean? Games are mastered in sRGB for SDR and in DCI-P3 for HDR, so the M10S II can reproduce every unique color in every game you play.</p><p>Now, an ultra-wide gamut can cause oversaturation in those spaces, resulting in an unnatural, neon effect. So, more technically isn’t always better. But while colors appear very bold and vivid on the M10S II, they didn’t feel overdone. And besides, there are sRGB and Cinema (for DCI-P3) color modes if you want to ensure strict accuracy to those gamuts.</p><h2 id="sony-s-typically-premium-design">Sony’s typically premium design</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="TKK6c4T63tdmtZJU3kNfba" name="Sony_M10S2_ 8.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/TKK6c4T63tdmtZJU3kNfba-1920-80.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>Unsurprisingly, as this is a Sony product, the M10S II is built extremely well. The monitor looks and feels premium, with high-quality materials used throughout.</p><p>The rear of the display is finished in textured metal rather than plastic. The stand and base are also made from metal and feel extremely sturdy. It simply feels built to last, and there’s a three-year warranty to back it up anyway.</p><h2 id="arguably-good-value">Arguably good value</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="LarUzNpMtjrLRDAFWrQmuZ" name="Sony_M10S2_ 6.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/LarUzNpMtjrLRDAFWrQmuZ-1920-80.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>This might sound a little odd given that the M10S II costs <a href="https://electronics.sony.com/tv-video/gaming-monitors/all-inzone-monitors/p/sdm27q102-b" target="_blank" rel="nofollow"><u>$1,099</u></a> / <a href="https://www.amazon.co.uk/Sony-INZONE-M10S-Gaming-Monitor/dp/B0GVJFDTB3/" target="_blank" rel="nofollow"><u>£1,199</u></a>, but I’d argue it’s fairly good value. It’s not uncommon for gaming monitors to push well beyond the $1,000 mark. The <a href="https://www.tomsguide.com/computing/monitors/lenovo-legion-pro-34wd-10-oled-review"><u>Lenovo Legion Pro 34WD-10 OLED</u></a>, for example, launched at $1,099. The <a href="https://www.tomsguide.com/reviews/samsung-odyssey-oled-g9"><u>Samsung Odyssey OLED G9</u></a>, meanwhile, launched at $1,799.</p><p>I know: those are ultrawides, and their prices have dropped since launch. But what I mean here is that the M10S II isn’t priced beyond the pale. And yet it offers superb performance for elite gamers. If you’re one of those people, the M10S II could make strong financial and competitive sense.</p><h2 class="article-body__section" id="section-sony-inzone-m10s-ii-review-the-downs"><span>Sony Inzone M10S II review: The downs</span></h2><p>The Inzone M10S II doesn’t have many faults. However, it’s not the brightest OLED we’ve lab tested, and the USB ports on the underside are pretty dated.</p><h2 id="not-the-brightest">Not the brightest</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="HZwVkRiBJBLqzXttnbMSga" name="Sony_M10S2_ 7.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/HZwVkRiBJBLqzXttnbMSga-1920-80.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>We don’t like to rely on manufacturer claims, which are often best-case scenarios and can be a tad (to say the least) unrealistic. As such, as part of our lab testing process, we also measure peak SDR and HDR brightness.</p><div ><table><thead><tr><th class="firstcol " ><p><strong>Product name</strong></p></th><th  ><p><strong>Panel type</strong></p></th><th  ><p><strong>Nits (HDR, at 10%)</strong></p></th><th  ><p><strong>Nits (SDR)</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Sony Inzone M10S II</strong></p></td><td  ><p>OLED</p></td><td  ><p>146 </p></td><td  ><p>231</p></td></tr><tr><td class="firstcol " ><p><strong>MSI MPG 272URX QD-OLED</strong></p></td><td  ><p>OLED</p></td><td  ><p>449 </p></td><td  ><p>254</p></td></tr><tr><td class="firstcol " ><p><strong>Lenovo Legion Pro 34WD10</strong></p></td><td  ><p>OLED</p></td><td  ><p>443</p></td><td  ><p>265</p></td></tr><tr><td class="firstcol " ><p><strong>Alienware AW2725Q</strong></p></td><td  ><p>QD-OLED</p></td><td  ><p>334</p></td><td  ><p>250 </p></td></tr><tr><td class="firstcol " ><p><strong>MSI MAG 272 QP</strong></p></td><td  ><p>OLED</p></td><td  ><p>284 </p></td><td  ><p>289</p></td></tr><tr><td class="firstcol " ><p><strong>MSI MPG 341CQR QD-OLED X36</strong></p></td><td  ><p>QD-OLED</p></td><td  ><p>413</p></td><td  ><p>280</p></td></tr></tbody></table></div><p>As you can see in the test results, the M10S II isn’t as bright as other OLED gaming panels we’ve tested and is well below the (rather ambitious) 1,500-nit peak brightness claimed by Sony.</p><p>Still, when you think about it, this monitor’s primary purpose is to be used in a dark room. And besides, figures alone don’t always tell the full story, and I certainly wouldn’t let our measured peak brightness put you off buying this monitor. I’ve had no issues using it in bright environments, largely thanks to the anti-glare coating. And I never struggled with darker in-game scenarios, thanks to the different color modes and black point equalizer.</p><h2 id="dated-usb-ports">Dated USB ports</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="QMNtiWvm7wPDH9KhHvZa3a" name="Sony_M10S2_ 9.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/QMNtiWvm7wPDH9KhHvZa3a-1920-80.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 only other gripe with the M10S II is that it uses fairly dated ports, at least when it comes to USB (with HDMI 2.1 and DisplayPort 2.1, the video ports are just fine). There are two USB-A downstream ports and a USB-B upstream port. These are outdated, and it would’ve been nice to see at least a USB-C port or two.</p><p>Again, it’s important to remember use cases here. I’m not going to complain about the lack of a Thunderbolt port or KVM controller on a monitor that isn’t designed for productivity, but come on, it’s 2026… how can Sony fit a Type-B but not a single Type-C?</p><h2 class="article-body__section" id="section-sony-inzone-m10s-ii-review-verdict"><span>Sony Inzone M10S II 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="xdncXHJiq6rPWsCo2R3LFa" name="Sony_M10S2_.JPG" alt="The Sony Inzone M10S II against a blue gradient background" src="https://cdn.mos.cms.futurecdn.net/xdncXHJiq6rPWsCo2R3LFa-1920-80.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 you want the fastest and most feature-packed gaming monitor around, especially if you’re an elite competitive player or aspiring pro, I wholeheartedly recommend the Sony Inzone M10S II.</p><p>Its flaws are very few and fairly minor. What’s more, it not only delivers blazing-fast hardware performance but also a huge roster of genuinely useful gaming tools and thoughtful quality-of-life features! Then there’s Sony’s usual build quality and a fairly reasonable price tag, all things considered. I’m genuinely rather sad to be giving it back to Sony!</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The new Sony Bravia 6 OLED TV is the LG C6 competitor I've been waiting for ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Sony just dropped a brand-new OLED TV, and the most surprising thing about it (other than its sudden, mid-year announcement) is its relatively low price.</p><p>For the first time, a Sony OLED TV is going toe to toe with more modestly priced OLEDs from Sony’s top rivals. The Bravia 6 is $1,999 for a 65-inch model, which puts it roughly in line with mid-range OLEDs from LG and Samsung.</p><p>That’s exciting news for anyone looking for more OLED-driven options in this price range, but I’m especially interested in the Bravia 6 for two reasons: its hardware and its picture processing.</p><div class="product"><a data-dimension112="6ff900ac-ac86-11f1-944f-f1e0f924b6c8" data-action="Deal Block" data-label="Bravia 6 4K OLED TV" data-dimension48="Bravia 6 4K OLED TV" data-dimension25="$1999" href="https://www.bestbuy.com/product/sony-65-class-bravia-6-oled-4k-hdr-smart-google-tv-with-gemini/J7XSRH5FG5" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="o2zzPr8hyKrVuVPesPs6t9" name="Sony-Bravia-6-OLED-TV-16-by-9-deal-block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/o2zzPr8hyKrVuVPesPs6t9-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/sony-65-class-bravia-6-oled-4k-hdr-smart-google-tv-with-gemini/J7XSRH5FG5" target="_blank" rel="nofollow" data-dimension112="6ff900ac-ac86-11f1-944f-f1e0f924b6c8" data-action="Deal Block" data-label="Bravia 6 4K OLED TV" data-dimension48="Bravia 6 4K OLED TV" data-dimension25="$1999">Bravia 6 4K OLED TV: $1999</a></strong><br>Sony's most affordable OLED TV of its 2026 Bravia lineup is the first to feature a full slate of four HDMI 2.1 inputs, all of which support 4K gaming at 120Hz. Sony's top-notch picture processing elevates the Bravia 6 above affordable OLEDs from rival brands.<a class="view-deal button" href="https://www.bestbuy.com/product/sony-65-class-bravia-6-oled-4k-hdr-smart-google-tv-with-gemini/J7XSRH5FG5" target="_blank" rel="nofollow" data-dimension112="6ff900ac-ac86-11f1-944f-f1e0f924b6c8" data-action="Deal Block" data-label="Bravia 6 4K OLED TV" data-dimension48="Bravia 6 4K OLED TV" data-dimension25="$1999">View Deal</a></p></div><h2 id="the-bravia-6-is-the-first-sony-oled-tv-with-four-hdmi-2-1-inputs">The Bravia 6 is the first Sony OLED TV with four HDMI 2.1 inputs</h2><p>Believe it or not, there aren’t very many Sony TVs with more than two HDMI 2.1 inputs. Despite the steep price tags associated with top-tier Sony sets, even the brand’s <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> (like the recently released <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-9-ii-true-rgb-tv-review">Bravia 9 II</a>) are mostly equipped with just a pair of high-bandwidth ports.</p><p>The Bravia 6 is different. It might not be as bright or as colorful as the <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-8-ii-review">Sony Bravia 8 II OLED</a>, but the Bravia 6 offers more flexibility when it comes to current- and next-generation gaming. With four HDMI 2.1-compatible inputs, folks who upgrade to the Bravia 6 won’t have to think too hard about what devices are plugged into which input, as <em>all</em> of them support the same slate of features, such as 4K gaming at 120Hz.</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.15%;"><img id="7n3NbVfxXMtxrmDNNbG6G3" name="LG-C6--12" alt="HDMI ports on a LG C6 OLED TV on a table" src="https://cdn.mos.cms.futurecdn.net/7n3NbVfxXMtxrmDNNbG6G3-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>For years, LG and Samsung OLED TVs have offered a full slate of HDMI 2.1 inputs. This has been true across nearly all of these brands’ OLEDs — from entry-level models to flagship-level models.</p><p>As expected, the Bravia 6’s closest competitors — the <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled-review">LG C6</a> and <a href="https://www.tomsguide.com/tvs/oled-tvs/samsung-s90h-oled-tv-review">Samsung S90H</a> — both carry four HDMI 2.1 inputs. Had Sony’s latest OLED TV arrived with just two high-bandwidth ports, it would have stumbled out of the gate.</p><p>But perhaps the most enticing aspect of the Bravia 6 isn't the hardware on its outside, but rather, the software that makes it all possible.</p><h2 id="sony-39-s-picture-processing-is-among-the-best-in-the-game">Sony's picture processing is among the best in the game</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:3008px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LpifwEvPHLE3gisyuowv4d" name="Setting 2 BRAVIA family-FNL" alt="The Sony 2025 lineup of TVs and soundbars" src="https://cdn.mos.cms.futurecdn.net/LpifwEvPHLE3gisyuowv4d-1920-80.jpg" mos="" align="middle" fullscreen="" width="3008" height="1692" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><div><blockquote><p>Sony's attention to detail could be the ace up its sleeve.</p></blockquote></div><p>Until now, experiencing Sony's exceptional processing on an OLED TV typically meant spending more for the privilege — at least relative to competitive sets. The Bravia 6's aggressive pricing makes the brand's expert engineering a bit more accessible.</p><p>If you’re the type of viewer who values an accurate presentation above all else, it's tough not to respect Sony's commitment to the cause. Even at the lowest end of the Bravia TV lineup (the <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-3-ii-review">Bravia 3 II</a>), Sony is dedicated to making the most of a TV’s hardware.</p><p>We're still in the process of evaluating the Bravia 6, and while we don't have test results ready to publish, it's quite possible that Sony's new OLED doesn't get quite as bright as mid-range models from LG and Samsung. But if the Bravia 6's picture processing is as good as we've seen on several Bravia TVs this year, Sony's attention to detail could be the ace up its sleeve.</p><h2 id="sony-bravia-6-outlook">Sony Bravia 6: Outlook</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:1367px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Kse7LEw8fXkEsjf2atrmBh" name="Bravia 6 OLED-3" alt="The Sony Bravia 6 OLED TV displaying abstract, colorful imagery from a wall-mounted position in a well-lit living room" src="https://cdn.mos.cms.futurecdn.net/Kse7LEw8fXkEsjf2atrmBh-1920-80.jpg" mos="" align="middle" fullscreen="" width="1367" height="769" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>As mentioned, we're still in the process of evaluating the Bravia 6 OLED. However, the TV's surprise announcement coincided with its debut on store shelves.</p><p>The 65-inch Bravia 6 is $1,999, but in the coming months, sale pricing could very well make the Bravia 6 a sleeper hit of the 2026 holiday-shopping season. Nevertheless, its current price makes it an intriguing alternative to the LG C6 and the Samsung S90H.</p><p>It's worth mentioning, too, that Sony recently emerged at the front of our assessment of the <a href="https://www.tomsguide.com/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up">best TV brands</a>. When we asked our readers to vote for the TV brand they trust the most, Sony ran away with those results, too.</p><p>Despite the tendency for Sony-branded TVs to carry higher price tags than rival sets, it seems that Sony has maintained a sterling reputation among A/V enthusiasts and casual viewers alike.</p><p>If the Bravia 6 can succeed <em>without</em> the high-price caveat, it could end up being one of the year's smartest buys for anyone in the market for a sensibly priced OLED TV.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.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/tvs/theres-no-way-around-it-sony-tvs-are-priced-higher-than-most-of-the-competition-heres-what-sonys-doing-right-what-its-doing-wrong-and-what-needs-to-change-in-2026">Sony TV report card 2026: Is the 'Sony tax' still worth paying?</a></li><li><a href="https://www.tomsguide.com/tvs/tcl-and-sony-are-joining-forces-to-sell-more-tvs-but-is-that-bad-news-for-shoppers">TCL and Sony are joining forces to sell more TVs — but is that bad news for shoppers?</a></li><li><a href="https://www.tomsguide.com/tvs/we-asked-readers-which-tv-brand-they-trusted-most-and-one-company-rose-above-the-rest">We asked readers which TV brand they trusted most — and one company rose above the rest</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/oled-tvs/the-new-sony-bravia-6-oled-tv-is-the-lg-c6-competitor-ive-been-waiting-for</link>
                                                                            <description>
                            <![CDATA[ The all-new Bravia 6 is Sony's most affordable OLED TV, and it's sure to give the LG C6 and Samsung S90H a run for their money. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">FGpwuCrRBff6nSc78ZBKy3</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/vMWyy9sNoYS5exE9n6LEu6-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 10 Sep 2026 09:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[OLED TVs]]></category>
                                                    <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ michael.desjardin@futurenet.com (Michael Desjardin) ]]></author>                    <dc:creator><![CDATA[ Michael Desjardin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/esytxRqW9GNVZNVCf7BFyV-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael Desjardin is a Senior Editor for TVs at Tom&#039;s Guide. He&#039;s been testing and tinkering with TVs professionally for over a decade, previously for Reviewed and USA Today. Michael studied media production and screenwriting at Emerson College. He loves cooking, zoning out to ambient music, and getting way too invested in the Red Sox. He considers himself living proof that TV doesn&#039;t necessarily rot your brain.&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/vMWyy9sNoYS5exE9n6LEu6-1920-80.jpg">
                                                            <media:credit><![CDATA[Sony]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Sony Bravia 6 OLED TV displaying colorful, abstract imagery from atop a credenza in a dimly lit living room]]></media:description>                                                            <media:text><![CDATA[The Sony Bravia 6 OLED TV displaying colorful, abstract imagery from atop a credenza in a dimly lit living room]]></media:text>
                                <media:title type="plain"><![CDATA[The Sony Bravia 6 OLED TV displaying colorful, abstract imagery from atop a credenza in a dimly lit living room]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/vMWyy9sNoYS5exE9n6LEu6-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Sony just dropped a brand-new OLED TV, and the most surprising thing about it (other than its sudden, mid-year announcement) is its relatively low price.</p><p>For the first time, a Sony OLED TV is going toe to toe with more modestly priced OLEDs from Sony’s top rivals. The Bravia 6 is $1,999 for a 65-inch model, which puts it roughly in line with mid-range OLEDs from LG and Samsung.</p><p>That’s exciting news for anyone looking for more OLED-driven options in this price range, but I’m especially interested in the Bravia 6 for two reasons: its hardware and its picture processing.</p><div class="product"><a data-dimension112="6ff900ac-ac86-11f1-944f-f1e0f924b6c8" data-action="Deal Block" data-label="Bravia 6 4K OLED TV" data-dimension48="Bravia 6 4K OLED TV" data-dimension25="$1999" href="https://www.bestbuy.com/product/sony-65-class-bravia-6-oled-4k-hdr-smart-google-tv-with-gemini/J7XSRH5FG5" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="o2zzPr8hyKrVuVPesPs6t9" name="Sony-Bravia-6-OLED-TV-16-by-9-deal-block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/o2zzPr8hyKrVuVPesPs6t9-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/sony-65-class-bravia-6-oled-4k-hdr-smart-google-tv-with-gemini/J7XSRH5FG5" target="_blank" rel="nofollow" data-dimension112="6ff900ac-ac86-11f1-944f-f1e0f924b6c8" data-action="Deal Block" data-label="Bravia 6 4K OLED TV" data-dimension48="Bravia 6 4K OLED TV" data-dimension25="$1999">Bravia 6 4K OLED TV: $1999</a></strong><br>Sony's most affordable OLED TV of its 2026 Bravia lineup is the first to feature a full slate of four HDMI 2.1 inputs, all of which support 4K gaming at 120Hz. Sony's top-notch picture processing elevates the Bravia 6 above affordable OLEDs from rival brands.<a class="view-deal button" href="https://www.bestbuy.com/product/sony-65-class-bravia-6-oled-4k-hdr-smart-google-tv-with-gemini/J7XSRH5FG5" target="_blank" rel="nofollow" data-dimension112="6ff900ac-ac86-11f1-944f-f1e0f924b6c8" data-action="Deal Block" data-label="Bravia 6 4K OLED TV" data-dimension48="Bravia 6 4K OLED TV" data-dimension25="$1999">View Deal</a></p></div><h2 id="the-bravia-6-is-the-first-sony-oled-tv-with-four-hdmi-2-1-inputs">The Bravia 6 is the first Sony OLED TV with four HDMI 2.1 inputs</h2><p>Believe it or not, there aren’t very many Sony TVs with more than two HDMI 2.1 inputs. Despite the steep price tags associated with top-tier Sony sets, even the brand’s <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> (like the recently released <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-9-ii-true-rgb-tv-review">Bravia 9 II</a>) are mostly equipped with just a pair of high-bandwidth ports.</p><p>The Bravia 6 is different. It might not be as bright or as colorful as the <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-8-ii-review">Sony Bravia 8 II OLED</a>, but the Bravia 6 offers more flexibility when it comes to current- and next-generation gaming. With four HDMI 2.1-compatible inputs, folks who upgrade to the Bravia 6 won’t have to think too hard about what devices are plugged into which input, as <em>all</em> of them support the same slate of features, such as 4K gaming at 120Hz.</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.15%;"><img id="7n3NbVfxXMtxrmDNNbG6G3" name="LG-C6--12" alt="HDMI ports on a LG C6 OLED TV on a table" src="https://cdn.mos.cms.futurecdn.net/7n3NbVfxXMtxrmDNNbG6G3-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>For years, LG and Samsung OLED TVs have offered a full slate of HDMI 2.1 inputs. This has been true across nearly all of these brands’ OLEDs — from entry-level models to flagship-level models.</p><p>As expected, the Bravia 6’s closest competitors — the <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled-review">LG C6</a> and <a href="https://www.tomsguide.com/tvs/oled-tvs/samsung-s90h-oled-tv-review">Samsung S90H</a> — both carry four HDMI 2.1 inputs. Had Sony’s latest OLED TV arrived with just two high-bandwidth ports, it would have stumbled out of the gate.</p><p>But perhaps the most enticing aspect of the Bravia 6 isn't the hardware on its outside, but rather, the software that makes it all possible.</p><h2 id="sony-39-s-picture-processing-is-among-the-best-in-the-game">Sony's picture processing is among the best in the game</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:3008px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LpifwEvPHLE3gisyuowv4d" name="Setting 2 BRAVIA family-FNL" alt="The Sony 2025 lineup of TVs and soundbars" src="https://cdn.mos.cms.futurecdn.net/LpifwEvPHLE3gisyuowv4d-1920-80.jpg" mos="" align="middle" fullscreen="" width="3008" height="1692" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><div><blockquote><p>Sony's attention to detail could be the ace up its sleeve.</p></blockquote></div><p>Until now, experiencing Sony's exceptional processing on an OLED TV typically meant spending more for the privilege — at least relative to competitive sets. The Bravia 6's aggressive pricing makes the brand's expert engineering a bit more accessible.</p><p>If you’re the type of viewer who values an accurate presentation above all else, it's tough not to respect Sony's commitment to the cause. Even at the lowest end of the Bravia TV lineup (the <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-3-ii-review">Bravia 3 II</a>), Sony is dedicated to making the most of a TV’s hardware.</p><p>We're still in the process of evaluating the Bravia 6, and while we don't have test results ready to publish, it's quite possible that Sony's new OLED doesn't get quite as bright as mid-range models from LG and Samsung. But if the Bravia 6's picture processing is as good as we've seen on several Bravia TVs this year, Sony's attention to detail could be the ace up its sleeve.</p><h2 id="sony-bravia-6-outlook">Sony Bravia 6: Outlook</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:1367px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Kse7LEw8fXkEsjf2atrmBh" name="Bravia 6 OLED-3" alt="The Sony Bravia 6 OLED TV displaying abstract, colorful imagery from a wall-mounted position in a well-lit living room" src="https://cdn.mos.cms.futurecdn.net/Kse7LEw8fXkEsjf2atrmBh-1920-80.jpg" mos="" align="middle" fullscreen="" width="1367" height="769" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>As mentioned, we're still in the process of evaluating the Bravia 6 OLED. However, the TV's surprise announcement coincided with its debut on store shelves.</p><p>The 65-inch Bravia 6 is $1,999, but in the coming months, sale pricing could very well make the Bravia 6 a sleeper hit of the 2026 holiday-shopping season. Nevertheless, its current price makes it an intriguing alternative to the LG C6 and the Samsung S90H.</p><p>It's worth mentioning, too, that Sony recently emerged at the front of our assessment of the <a href="https://www.tomsguide.com/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up">best TV brands</a>. When we asked our readers to vote for the TV brand they trust the most, Sony ran away with those results, too.</p><p>Despite the tendency for Sony-branded TVs to carry higher price tags than rival sets, it seems that Sony has maintained a sterling reputation among A/V enthusiasts and casual viewers alike.</p><p>If the Bravia 6 can succeed <em>without</em> the high-price caveat, it could end up being one of the year's smartest buys for anyone in the market for a sensibly priced OLED TV.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.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/tvs/theres-no-way-around-it-sony-tvs-are-priced-higher-than-most-of-the-competition-heres-what-sonys-doing-right-what-its-doing-wrong-and-what-needs-to-change-in-2026">Sony TV report card 2026: Is the 'Sony tax' still worth paying?</a></li><li><a href="https://www.tomsguide.com/tvs/tcl-and-sony-are-joining-forces-to-sell-more-tvs-but-is-that-bad-news-for-shoppers">TCL and Sony are joining forces to sell more TVs — but is that bad news for shoppers?</a></li><li><a href="https://www.tomsguide.com/tvs/we-asked-readers-which-tv-brand-they-trusted-most-and-one-company-rose-above-the-rest">We asked readers which TV brand they trusted most — and one company rose above the rest</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I put the Sony Bravia 3 II through its paces — and it's an easy recommendation under $1,000 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Shelling out for one of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html"><u>best TVs</u></a> is a luxury in this tough economy — thankfully almost every manufacturer has a budget or mid-tier offering. For Sony, that’s the Bravia 3 II, a mid-range LED TV that sits above the entry-level Bravia 2 II and replaces the <a href="https://www.tomsguide.com/tvs/sony-bravia-3-led-tv-review"><u>Bravia 3</u></a> that came before it. </p><p>We’ve already reviewed the <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-9-ii-true-rgb-tv-review"><u>Sony Bravia 9 II</u></a> flagship True RGB TV as well as the step-down <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review"><u>Sony Bravia 7 II</u></a>, but the Bravia 3 II recently made its way to our New York City test lab. Overall, it’s a great TV where Sony’s excellent processing meets a direct-lit LED panel at a midrange price point starting around $900. But is that good enough when you could also go with a cheaper Roku, Vizio, or an Amazon Fire TV?</p><p>After spending some time with this TV, and putting it through our extensive lab testing, the answer to that question is a resounding yes, especially if you’re planning on putting this TV in your living room for daytime viewing. Nighttime viewers — or folks with a dedicated, lighting-controlled space like a home theater — might want to keep the other aforementioned Sony TVs at the top of their shopping list instead.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-pricing-and-availability"><span>Sony Bravia 3 II review: Pricing and availability</span></h3><p>You can buy the Sony Bravia 3 II today from Sony’s website and other retailers like <a href="https://www.bestbuy.com/product/sony-65-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-and-anti-glare-screen-2026/J7XSRH54PY"><u>Best Buy</u></a>. Pricing starts at <a href="https://www.bestbuy.com/product/sony-43-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54S3"><u>$569 for a 43-inch model</u></a> and goes all the way up to <a href="https://www.bestbuy.com/product/sony-100-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54Y2"><u>$1,999 for a 100-inch model</u></a>. Our 65-inch review unit’s MSRP is $900, but it can be found <a href="https://www.bestbuy.com/product/sony-65-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-and-anti-glare-screen-2026/J7XSRH54PY"><u>on sale for $849</u></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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="uBGFHiMMLgDXDsogk9jYe5" name="Sony-Bravia-3-II--19" alt="The back of a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/uBGFHiMMLgDXDsogk9jYe5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Pricing for the full range is below.</p><ul><li>43-inch: $599 | <a href="https://www.bestbuy.com/product/sony-43-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54S3"><u>Sale: $569</u></a></li><li>50-inch: $699 | <a href="https://www.bestbuy.com/product/sony-50-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54PF"><u>Sale $669</u></a></li><li>55-inch: $799 | <a href="https://www.bestbuy.com/product/sony-55-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54PH"><u>Sale $699</u></a></li><li>65-inch: $899 | <a href="https://www.bestbuy.com/product/sony-65-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-and-anti-glare-screen-2026/J7XSRH54PY"><u>Sale $849</u></a> [TESTED]</li><li>75-inch: $1,999 | <a href="https://www.bestbuy.com/product/sony-75-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-and-anti-glare-screen-2026/J7XSRH54YT"><u>Sale $1,049</u></a></li><li>85-inch: $1,599 | <a href="https://www.bestbuy.com/product/sony-85-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54YP"><u>Sale $1,499</u></a></li><li>100-inch: $3,099 | <a href="https://www.bestbuy.com/product/sony-100-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54Y2"><u>Sale $1,999</u></a></li></ul><p>The 65-inch Bravia 3 II is $100 cheaper than the older Bravia 3 when it launched in 2024 for the price of $999, but that TV is now harder to find. </p><p>It’s worth noting that Sony’s TVs — even at this entry-level price — are more expensive than their competitors. For example, you can pick up a 65-inch <a href="https://www.tomsguide.com/tvs/qled-tvs/roku-65-select-series-4k-qled-tv-review"><u>Roku Select Series</u></a> for $449, or the <a href="https://www.tomsguide.com/tvs/4k-tvs/vizio-mini-led-quantum-4k-smart-tv-review"><u>Vizio Mini-LED Quantum 4K Smart TV </u></a>for $398. Those other options might tempt you with their lower sticker price, but choosing another brand means passing up on Sony’s excellent picture processing.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-design"><span>Sony Bravia 3 II review: Design</span></h3><p>The Bravia 3 II is a simple, super clean-looking TV that is easy to set up. The 65-inch model has a decent size footprint of 57 x 35.56  x 11.87 inches with the stand, and weighs about 46 pounds without 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="wGcdNdga6hBq6ZNa3uYVT5" name="Sony-Bravia-3-II--16" alt="The side of a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/wGcdNdga6hBq6ZNa3uYVT5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Bezels are also thin, coming in around a quarter of an inch. You have about 2 inches of space under the front bezels of the TV, and there’s very minimal branding aside from a Sony logo on the bottom left. </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.15%;"><img id="Wiyvvu7R9UMaXDo6Exxtc5" name="Sony-Bravia-3-II--15" alt="The power button on a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/Wiyvvu7R9UMaXDo6Exxtc5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The middle of the TV has a chin for the light sensors, power button, and a microphone slider button to mute the TV’s voice control features — a surprising feature that the more expensive Bravia 7 II also has onboard.</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.15%;"><img id="mFSSoX5LH5BFcygC5of6j5" name="Sony-Bravia-3-II--20" alt="The left-hand foot of a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/mFSSoX5LH5BFcygC5of6j5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The feet, which have a plastic bottom and metal top, slide into slots at the bottom of the TV and are secured with screws with little effort. Unfortunately, those feet must be placed at either the right or left side under the panel, and can not be center-mounted. </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.15%;"><img id="KjYuywykAjtVsKDgf6fDo5" name="Sony-Bravia-3-II--21" alt="The right-hand foot of a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/KjYuywykAjtVsKDgf6fDo5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You’ll need a wide entertainment center for this set, or you can wall mount using the 300mm x 300mm VESA mount on the rear. The rear of the TV isn’t too busy — it’s all plastic, all-black, and nothing fancy. Ports, though, are split into two areas and are a little surprising. </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.15%;"><img id="Nyqnjf7CTarLbynzJMPKi5" name="Sony-Bravia-3-II--18" alt="The ports on a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/Nyqnjf7CTarLbynzJMPKi5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You’ll find four total HDMI 2.1 ports on this TV, something even Sony’s Flagship TVs don't have, making this an excellent companion for gaming consoles. HDMI 1 and HDMI 2 sit on the right side of the TV, directly below the USB 2.0 and USB 3.0 ports. </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.15%;"><img id="QanzoMXdVqSTscRLdqooo5" name="Sony-Bravia-3-II--17" alt="The ports on a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/QanzoMXdVqSTscRLdqooo5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>HDMI 3 and 4, meanwhile, are tucked towards the middle in a little cove, and face downward. The eARC port is attached to HDMI 3, which is convenient for soundbars. The other mix of ports in that area are the Digital Audio Output, RF port, Ethernet port, and the RS-232C input. </p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-how-we-test"><span>Sony Bravia 3 II review: How we test</span></h3><p>Testing TVs for Tom’s Guide is an in-depth process. In our New York City test lab, my lab assistant and I personally test every TV across SDR and HDR benchmarks using the 10% window method. For color accuracy, we switch the TV over to Filmmaker Mode, usually the “best” mode on the TV first. We also disable any intrusive ambient light sensors that could skew our meter’s readings.</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.15%;"><img id="z9qpiWtks64ixxEWPG5Mb5" name="Sony-Bravia-3-II--7" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/z9qpiWtks64ixxEWPG5Mb5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>For our tools, we use the Jeti spectraval 1501-HiRes spectroradiometer to profile the Klein K10-A colorimeter. Once profiled, these work with the Murideo 8K-SIX-G Metal pattern generator and a Tom’s-Guide specific workflow in <a href="https://www.portrait.com/calman-home/"><u>Portrait Displays’ Calman Ultimate software</u></a> which helps us determine the TV’s overall brightness and color accuracy across HDR and SDR.</p><p>For testing gaming performance, we use a Leo Bodnar 4K Video Signal Lag Tester. This tool is used on the screen in the TV’s Game Mode to measure how responsive the TV really is. Once all technical lab testing is done, and we’ve tested the majority of the TV’s remaining picture modes, we put the TV through subjective testing using a curated selection of films and shows, both on streaming services, and on our lab’s Blu-ray player. </p><p>Visit our “<a href="https://www.tomsguide.com/us/how-we-test-tvs,review-2478.html"><u>How we test TVs</u></a>” guide for a more in-depth explanation on the process.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-performance"><span>Sony Bravia 3 II review: Performance</span></h3><p>The Bravia 3 II uses Sony's XR Processor, which is the same processor family found in the company's more expensive flagship TVs. Needless to say, it does the job excellently, helping optimize color, contrast and fine detail. </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.15%;"><img id="eEPBwAFX7JyjtHUVakMpf5" name="Sony-Bravia-3-II--8" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/eEPBwAFX7JyjtHUVakMpf5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I noticed this in action across all the HDR content I watched, including <em>Project Hail Mary </em>on Amazon Prime and <em>Top Gun Maverick. </em>The opening 10 minutes of <em>Project Hail Mary</em> has some dark scenes but plenty of beautiful shots of the space ship's interior. The Bravia 3 II brings out lots of details as Grace plays with the ship’s various machines. Finer details, like the skin tone in Grace’s face looked natural and even the bright objects in the cold metallic ship have pop and life to them. </p><p>But this is still an LED TV, however, and Sony’s image processor does not overcome the limits of the panel on the TV itself, particularly with black levels. Without local dimming, darker scenes and select darker colors don’t have a lot of depth. For example, I noticed that when Grace looked out of the spacecraft window, the deep darkness of space had more of a dark grayish look.</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.15%;"><img id="WfYipnDpGRqx5sSeP4MCa5" name="Sony-Bravia-3-II--5" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/WfYipnDpGRqx5sSeP4MCa5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>For a more controlled test, I turned to watching <em>Top Gun: Maverick</em> on our lab’s 4K Blu-ray player. As with <em>Project Hail Mary,</em> the movie looked surprisingly good in regular room lighting. The bright outdoor shots in the movie’s opening made the TV look better than its price — and brighter than my measured 520 nits in Professional Mode might show.</p><p>I noticed the fine details I never caught before: the drops of sweat on Tom Cruise’s hands as he worked on his jet, and as the bright sunlight shimmering on the fuselage on an airplane as the camera panned to it in the hangar. </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.15%;"><img id="HQgSXEBf8nHU5B3n7cpkf5" name="Sony-Bravia-3-II--6" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/HQgSXEBf8nHU5B3n7cpkf5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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, again, the lack of deep blacks that a Mini-LED or QLED TV might have are still present. As Tom Cruise got dressed and put on his pilot outfit, the blacks in the fabric looked slightly dull. Scenes still remained enjoyable though, just you will not get the reference-grade HDR brightness and accuracy you might get on the Bravia 9 II or more premium TVs.</p><p>Conversely, SDR performance is solid, especially with New York Mets MLB replays, and regular cartoons like <em>Spongebob Squarepants</em> on YouTube. </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.15%;"><img id="UV3N3soiXvouaf8E5yN9i5" name="Sony-Bravia-3-II--9" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/UV3N3soiXvouaf8E5yN9i5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>For SDR content, I watched about 20 minutes of a 45-minute-long <a href="https://www.youtube.com/watch?v=5VN6R4wox7E"><u>Pure Happiness at the Krusty Krab video.</u></a> On YouTube. Switching the Bravia 3 II to Vivid mode, I was blown away. It was my favorite way of testing the TV. </p><p>Spongebob’s skin popped with a bright vibrant yellow that I’ve never seen before, even on an OLED TV. The blues in a cabinet in the Krusty Krab’s kitchen, and the turquoise in Squidward's skin looked incredible. Even the red in Mr. Krabs’ shell jumped off the screen and appealed to my eyes right away.</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.15%;"><img id="ZWCnmnk8VptNDARP6KKhi5" name="Sony-Bravia-3-II--4" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/ZWCnmnk8VptNDARP6KKhi5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>This episode looked really bold and bright, and it was not what I expected out of this TV. Sony’s image processing really wins here, producing a more polished picture than I’ve seen from many budget TVs from Roku, Amazon and Vizio.</p><p>Of course, Vivid mode is designed to push brightness higher. It doesn’t go for the pure color accuracy which is where Professional mode is better. But the extra brightness really helps make cartoons, or animated series, and even sports look great on this TV.  </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.15%;"><img id="CQMEPcfbhoEGDYcHxKvFn5" name="Sony-Bravia-3-II--2" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/CQMEPcfbhoEGDYcHxKvFn5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I also switched the TV over to Standard mode and watched MLB highlights of a New York Mets game. The green grass of the MLB diamond looked alive. The “road gray” color in Kodai Senga’s Mets uniform was also great. I could make out the fine folds in his uniform as he threw out the final pitch to get his first career save. </p><p>Overall, there were a few snags with the black levels, but I was impressed with the Sony Bravia 3 II’s performance.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-test-results"><span>Sony Bravia 3 II review: Test results</span></h3><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p>Sony Bravia 3 II</p></td><td  ><p>Sony Bravia 3</p></td><td  ><p>Roku Select Series</p></td><td  ><p>Vizio Mini LED Quantum </p></td><td  ><p>Sony Bravia 7 II </p></td></tr><tr><td class="firstcol " ><p><strong>SDR Brightness (10%, in nits)</strong></p></td><td  ><p>352</p></td><td  ><p>397</p></td><td  ><p>334</p></td><td  ><p>218* </p></td><td  ><p>100</p></td></tr><tr><td class="firstcol " ><p><strong>Delta-E (lower is better)</strong></p></td><td  ><p>2.17</p></td><td  ><p>3.49</p></td><td  ><p>2.45</p></td><td  ><p>4.63 </p></td><td  ><p>4.79</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 709 Gamut Coverage</strong></p></td><td  ><p>98.81%</p></td><td  ><p>99.72%</p></td><td  ><p>93.85%</p></td><td  ><p>96.82%</p></td><td  ><p>99.94%</p></td></tr><tr><td class="firstcol " ><p><strong>HDR Brightness (10%, in nits)</strong></p></td><td  ><p>520</p></td><td  ><p>448</p></td><td  ><p>334</p></td><td  ><p>781  </p></td><td  ><p>2,077</p></td></tr><tr><td class="firstcol " ><p><strong>UHDA-P3 Gamut Coverage</strong></p></td><td  ><p>94.62%</p></td><td  ><p>95.88%</p></td><td  ><p>91.59%</p></td><td  ><p>92.94% </p></td><td  ><p>97.73%</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 2020 Gamut Coverage</strong></p></td><td  ><p>74.06%</p></td><td  ><p>69.81%</p></td><td  ><p>64.5%</p></td><td  ><p>71.37%</p></td><td  ><p>87.17%</p></td></tr><tr><td class="firstcol " ><p><strong>Input latency (in milliseconds)</strong></p></td><td  ><p>9.8</p></td><td  ><p>9.8</p></td><td  ><p>9.6</p></td><td  ><p>9.9</p></td><td  ><p>10.4</p></td></tr></tbody></table></div><p>As the lab results show, the 65-inch Bravia 3 II performs well for a direct-lit LED HDR TV. I measured 520 nits on a 10% window, which beats out the older 43-inch Bravia 3 (448 nits) — though that is a smaller size which impacts benchmarks. It also completely outperforms budget entry options like the Roku Select Series QLED (334 nits). </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.15%;"><img id="3NQGNw8UShFt9SsydrVfj5" name="Sony-Bravia-3-II--3" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/3NQGNw8UShFt9SsydrVfj5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>It, however, does not rival the Vizio Mini LED Quantum (781 nits), which has a different technology and the dimming zones this TV lacks. The flagship True RGB beast like the Bravia 7 II (2,077 nits) also won’t come close to it, but shows just how much more you can get for extra money.</p><p>Out of the box, the Bravia 3 II’s 2.12 Delta-E score in Professional Mode beats both the older Bravia 3 (3.49) and competing models like the Vizio Mini LED (4.63) This shows its strong color accuracy.</p><p>Not a lot of content is mastered for Rec 2020 coverage, but the Sony Bravia 3 II covers 74.06% of that gamut. That is a big jump from its smaller predecessor (69.81%). It is also ahead of the Roku QLED (64.50%), and the Vizio Mini LED (71.37%). For the more common UHDA-P3, it covers 94.62% of UHDA-P3, putting it third in the group, but these small percentage differences are not something your eyes will likely notice.  </p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-gaming"><span>Sony Bravia 3 II review: Gaming</span></h3><p>I was happy to see that the gaming experience on the Bravia 3 II’s game mode was smooth as butter — which is impressive, considering its sub-$1,000 price. Like the more expensive Bravia 7 II and Bravia 9 II, the TV supports Auto HDR Tone Mapping with PlayStation 5, along with VRR and ALLM support. This lets you skip the HDR optimization and auto applies the best settings for the TV’s HDR gaming features. </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.15%;"><img id="bx9cLPEDWZVJKpUfrYgkg5" name="Sony-Bravia-3-II--14" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/bx9cLPEDWZVJKpUfrYgkg5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Yet <em>unlike</em> the more expensive Bravia 7 II and Bravia 9 II, this TV has four HDMI 2.1 ports, meaning you can use up to four gaming consoles at 120Hz. For a gamer like me, that’s heaven. It is honestly very baffling to me why this TV has this many HDMI 2.1 ports and some of Sony's flagship TVs do not.</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.15%;"><img id="Wb6gUU9bz4bZseiji7x7k5" name="Sony-Bravia-3-II--13" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/Wb6gUU9bz4bZseiji7x7k5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>To test the gaming performance, I booted up <em>Pragamata</em> on our lab’s PlayStation 5. The game looked visually incredible, and played great. I experienced no lag in this game, where input timing is key. My quick inputs on the Triangle, Square, Circle buttons perfectly aligned with the hacking sequence needed to shoot enemy crusher robots in the Solar Power Plant sector.  </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.15%;"><img id="ZyvgxVkDRpUP2jdbrYCpi5" name="Sony-Bravia-3-II--11" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/ZyvgxVkDRpUP2jdbrYCpi5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Sony also includes the PS Remote Play app on the TV pre-installed. I have to admit, it was nice remoting into my own PS5 and playing a game like Gran Turismo 7 that we do not own on the lab’s console. </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.15%;"><img id="CgqzBjwNLDZ2wEKYEFfid5" name="Sony-Bravia-3-II--12" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/CgqzBjwNLDZ2wEKYEFfid5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>A dedicated Game Bar can also be summoned by pressing the “. . .” button on the remote when in the TV’s game mode. This gives you controls for VRR, Motion blur reduction, a Black equalizer, and the option to add a Crosshair. These are nice-to-have features at this TV’s price point.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-sound"><span>Sony Bravia 3 II review: Sound</span></h3><p>For audio testing, I watched the music videos for Post Malone’s “Sunflower” as well as <a href="https://www.youtube.com/watch?v=OgjzcwzIpyI"><u>Shaboozy’s “Highway”.</u></a> The Bravia 3 II sports "X-Balanced" speakers in a "Bass Reflex" enclosure. There's no subwoofers or surface drivers, so I would suggest pairing this TV with a soundbar if you plan to do anything beyond watching movies or TV shows.</p><p>On the first music video, I noticed how clear Post Malone’s vocals sounded. There was a lack of sub bass, but with the volume turned up to around 30%, I didn’t hear any distortion. </p><p>With Shaboozy’s music video, I heard the thump from his deeper voice, but the finer details from acoustic guitar was somewhat muted.</p><p>My recommendation? Check out our list of the <a href="https://www.tomsguide.com/audio/soundbars/best-soundbars"><u>best soundbars</u></a> to pair this set’s impressive performance with a more formidable piece of audio equipment.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-smart-tv-and-remote"><span>Sony Bravia 3 II review: Smart TV and remote</span></h3><p>We’ve reviewed almost a dozen TV this year with Google TV and the Bravia 3 II adds another tally to that list. Compared to what you'll have with a Roku TV or ad-ridden and privacy-last Vizio TV, Google TV is minimalist, clean and easy to use.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="DypMFjKQD8twXfKohCrNi5" name="Sony-Bravia-3-II--10" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/DypMFjKQD8twXfKohCrNi5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Everything you need is up front in a clean and easy to navigate experience, from settings, to inputs to TV shows and movies. The TV’s Google Gemini integration will have your back, too and will make finding what you’re looking for easy in a more human-friendly way. </p><p>Sony is recycling the remote from the Bravia 7 II on this year’s Bravia 3 II. It is a plastic remote, with a speckled design. It’s a bit big in the hands but still super comfortable to use. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="pDGMJLF9FMC2Q8wmWvw5o5" name="Sony-Bravia-3-II--22" alt="The remote for a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/pDGMJLF9FMC2Q8wmWvw5o5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>There’s the usual suite of buttons for navigation, apps like Disney+, Netflix, YouTube, voice control, and settings and menu. The remote can also be found using the power button on the TV. You also can program your own app to the star button on the remote. </p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-verdict"><span>Sony Bravia 3 II review: Verdict</span></h3><p>The Sony Bravia 3 II is a surprising TV. It brings Sony’s flagship-level image processing to a basic LED panel to help make your favorite TV shows and movies look great when the lights are on. It might not match Mini-LED or QLED TVs for brightness or black levels, but it’s still great for the everyday TV viewer. </p><p>The Bravia 3 II will be a solid purchase thanks to its gaming performance. It is great for gamers with its four HDMI 2.1 ports, its sleek Google TV interface, and a smart design. If you're buying a 65-inch TV primarily for daytime living-room viewing and gaming, this is the one for you. </p><p>If you want cheaper, sub-$500 options, the aforementioned Roku or Vizio models are good first ports of call, while the more-expensive Bravia 7 II will deliver better color and brightness. As a jack-of-all-trades, though, the Bravia 3 II is a good all-arounder.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-3-ii-review</link>
                                                                            <description>
                            <![CDATA[ The Sony Bravia 3 II impresses with vibrant, detail-rich images in well-lit rooms, thanks to Sony's excellent image processing. The TV also sports flagship-level features like multiple HDMI 2.1 ports and both VRR and ALLM support. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">nZLUHmWN8T5UGL5n5kcWSk</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/TuibEbV8L7bhy4VwZWxhd5-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 09 Sep 2026 13:43:44 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[4k TVs]]></category>
                                                    <category><![CDATA[TVs]]></category>
                                                                                                                    <dc:creator><![CDATA[ Arif Bacchus ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/45MeDeb7Xn988jfZfHW7UG-320-70.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/TuibEbV8L7bhy4VwZWxhd5-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A Sony Bravia 3 II TV on a desk]]></media:description>                                                            <media:text><![CDATA[A Sony Bravia 3 II TV on a desk]]></media:text>
                                <media:title type="plain"><![CDATA[A Sony Bravia 3 II TV on a desk]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/TuibEbV8L7bhy4VwZWxhd5-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Shelling out for one of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html"><u>best TVs</u></a> is a luxury in this tough economy — thankfully almost every manufacturer has a budget or mid-tier offering. For Sony, that’s the Bravia 3 II, a mid-range LED TV that sits above the entry-level Bravia 2 II and replaces the <a href="https://www.tomsguide.com/tvs/sony-bravia-3-led-tv-review"><u>Bravia 3</u></a> that came before it. </p><p>We’ve already reviewed the <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-9-ii-true-rgb-tv-review"><u>Sony Bravia 9 II</u></a> flagship True RGB TV as well as the step-down <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review"><u>Sony Bravia 7 II</u></a>, but the Bravia 3 II recently made its way to our New York City test lab. Overall, it’s a great TV where Sony’s excellent processing meets a direct-lit LED panel at a midrange price point starting around $900. But is that good enough when you could also go with a cheaper Roku, Vizio, or an Amazon Fire TV?</p><p>After spending some time with this TV, and putting it through our extensive lab testing, the answer to that question is a resounding yes, especially if you’re planning on putting this TV in your living room for daytime viewing. Nighttime viewers — or folks with a dedicated, lighting-controlled space like a home theater — might want to keep the other aforementioned Sony TVs at the top of their shopping list instead.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-pricing-and-availability"><span>Sony Bravia 3 II review: Pricing and availability</span></h3><p>You can buy the Sony Bravia 3 II today from Sony’s website and other retailers like <a href="https://www.bestbuy.com/product/sony-65-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-and-anti-glare-screen-2026/J7XSRH54PY"><u>Best Buy</u></a>. Pricing starts at <a href="https://www.bestbuy.com/product/sony-43-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54S3"><u>$569 for a 43-inch model</u></a> and goes all the way up to <a href="https://www.bestbuy.com/product/sony-100-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54Y2"><u>$1,999 for a 100-inch model</u></a>. Our 65-inch review unit’s MSRP is $900, but it can be found <a href="https://www.bestbuy.com/product/sony-65-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-and-anti-glare-screen-2026/J7XSRH54PY"><u>on sale for $849</u></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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="uBGFHiMMLgDXDsogk9jYe5" name="Sony-Bravia-3-II--19" alt="The back of a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/uBGFHiMMLgDXDsogk9jYe5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Pricing for the full range is below.</p><ul><li>43-inch: $599 | <a href="https://www.bestbuy.com/product/sony-43-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54S3"><u>Sale: $569</u></a></li><li>50-inch: $699 | <a href="https://www.bestbuy.com/product/sony-50-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54PF"><u>Sale $669</u></a></li><li>55-inch: $799 | <a href="https://www.bestbuy.com/product/sony-55-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54PH"><u>Sale $699</u></a></li><li>65-inch: $899 | <a href="https://www.bestbuy.com/product/sony-65-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-and-anti-glare-screen-2026/J7XSRH54PY"><u>Sale $849</u></a> [TESTED]</li><li>75-inch: $1,999 | <a href="https://www.bestbuy.com/product/sony-75-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-and-anti-glare-screen-2026/J7XSRH54YT"><u>Sale $1,049</u></a></li><li>85-inch: $1,599 | <a href="https://www.bestbuy.com/product/sony-85-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54YP"><u>Sale $1,499</u></a></li><li>100-inch: $3,099 | <a href="https://www.bestbuy.com/product/sony-100-class-bravia-3-ii-4k-hdr-led-google-tv-with-gemini-2026/J7XSRH54Y2"><u>Sale $1,999</u></a></li></ul><p>The 65-inch Bravia 3 II is $100 cheaper than the older Bravia 3 when it launched in 2024 for the price of $999, but that TV is now harder to find. </p><p>It’s worth noting that Sony’s TVs — even at this entry-level price — are more expensive than their competitors. For example, you can pick up a 65-inch <a href="https://www.tomsguide.com/tvs/qled-tvs/roku-65-select-series-4k-qled-tv-review"><u>Roku Select Series</u></a> for $449, or the <a href="https://www.tomsguide.com/tvs/4k-tvs/vizio-mini-led-quantum-4k-smart-tv-review"><u>Vizio Mini-LED Quantum 4K Smart TV </u></a>for $398. Those other options might tempt you with their lower sticker price, but choosing another brand means passing up on Sony’s excellent picture processing.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-design"><span>Sony Bravia 3 II review: Design</span></h3><p>The Bravia 3 II is a simple, super clean-looking TV that is easy to set up. The 65-inch model has a decent size footprint of 57 x 35.56  x 11.87 inches with the stand, and weighs about 46 pounds without 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="wGcdNdga6hBq6ZNa3uYVT5" name="Sony-Bravia-3-II--16" alt="The side of a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/wGcdNdga6hBq6ZNa3uYVT5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Bezels are also thin, coming in around a quarter of an inch. You have about 2 inches of space under the front bezels of the TV, and there’s very minimal branding aside from a Sony logo on the bottom left. </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.15%;"><img id="Wiyvvu7R9UMaXDo6Exxtc5" name="Sony-Bravia-3-II--15" alt="The power button on a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/Wiyvvu7R9UMaXDo6Exxtc5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The middle of the TV has a chin for the light sensors, power button, and a microphone slider button to mute the TV’s voice control features — a surprising feature that the more expensive Bravia 7 II also has onboard.</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.15%;"><img id="mFSSoX5LH5BFcygC5of6j5" name="Sony-Bravia-3-II--20" alt="The left-hand foot of a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/mFSSoX5LH5BFcygC5of6j5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The feet, which have a plastic bottom and metal top, slide into slots at the bottom of the TV and are secured with screws with little effort. Unfortunately, those feet must be placed at either the right or left side under the panel, and can not be center-mounted. </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.15%;"><img id="KjYuywykAjtVsKDgf6fDo5" name="Sony-Bravia-3-II--21" alt="The right-hand foot of a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/KjYuywykAjtVsKDgf6fDo5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You’ll need a wide entertainment center for this set, or you can wall mount using the 300mm x 300mm VESA mount on the rear. The rear of the TV isn’t too busy — it’s all plastic, all-black, and nothing fancy. Ports, though, are split into two areas and are a little surprising. </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.15%;"><img id="Nyqnjf7CTarLbynzJMPKi5" name="Sony-Bravia-3-II--18" alt="The ports on a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/Nyqnjf7CTarLbynzJMPKi5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You’ll find four total HDMI 2.1 ports on this TV, something even Sony’s Flagship TVs don't have, making this an excellent companion for gaming consoles. HDMI 1 and HDMI 2 sit on the right side of the TV, directly below the USB 2.0 and USB 3.0 ports. </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.15%;"><img id="QanzoMXdVqSTscRLdqooo5" name="Sony-Bravia-3-II--17" alt="The ports on a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/QanzoMXdVqSTscRLdqooo5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>HDMI 3 and 4, meanwhile, are tucked towards the middle in a little cove, and face downward. The eARC port is attached to HDMI 3, which is convenient for soundbars. The other mix of ports in that area are the Digital Audio Output, RF port, Ethernet port, and the RS-232C input. </p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-how-we-test"><span>Sony Bravia 3 II review: How we test</span></h3><p>Testing TVs for Tom’s Guide is an in-depth process. In our New York City test lab, my lab assistant and I personally test every TV across SDR and HDR benchmarks using the 10% window method. For color accuracy, we switch the TV over to Filmmaker Mode, usually the “best” mode on the TV first. We also disable any intrusive ambient light sensors that could skew our meter’s readings.</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.15%;"><img id="z9qpiWtks64ixxEWPG5Mb5" name="Sony-Bravia-3-II--7" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/z9qpiWtks64ixxEWPG5Mb5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>For our tools, we use the Jeti spectraval 1501-HiRes spectroradiometer to profile the Klein K10-A colorimeter. Once profiled, these work with the Murideo 8K-SIX-G Metal pattern generator and a Tom’s-Guide specific workflow in <a href="https://www.portrait.com/calman-home/"><u>Portrait Displays’ Calman Ultimate software</u></a> which helps us determine the TV’s overall brightness and color accuracy across HDR and SDR.</p><p>For testing gaming performance, we use a Leo Bodnar 4K Video Signal Lag Tester. This tool is used on the screen in the TV’s Game Mode to measure how responsive the TV really is. Once all technical lab testing is done, and we’ve tested the majority of the TV’s remaining picture modes, we put the TV through subjective testing using a curated selection of films and shows, both on streaming services, and on our lab’s Blu-ray player. </p><p>Visit our “<a href="https://www.tomsguide.com/us/how-we-test-tvs,review-2478.html"><u>How we test TVs</u></a>” guide for a more in-depth explanation on the process.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-performance"><span>Sony Bravia 3 II review: Performance</span></h3><p>The Bravia 3 II uses Sony's XR Processor, which is the same processor family found in the company's more expensive flagship TVs. Needless to say, it does the job excellently, helping optimize color, contrast and fine detail. </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.15%;"><img id="eEPBwAFX7JyjtHUVakMpf5" name="Sony-Bravia-3-II--8" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/eEPBwAFX7JyjtHUVakMpf5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I noticed this in action across all the HDR content I watched, including <em>Project Hail Mary </em>on Amazon Prime and <em>Top Gun Maverick. </em>The opening 10 minutes of <em>Project Hail Mary</em> has some dark scenes but plenty of beautiful shots of the space ship's interior. The Bravia 3 II brings out lots of details as Grace plays with the ship’s various machines. Finer details, like the skin tone in Grace’s face looked natural and even the bright objects in the cold metallic ship have pop and life to them. </p><p>But this is still an LED TV, however, and Sony’s image processor does not overcome the limits of the panel on the TV itself, particularly with black levels. Without local dimming, darker scenes and select darker colors don’t have a lot of depth. For example, I noticed that when Grace looked out of the spacecraft window, the deep darkness of space had more of a dark grayish look.</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.15%;"><img id="WfYipnDpGRqx5sSeP4MCa5" name="Sony-Bravia-3-II--5" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/WfYipnDpGRqx5sSeP4MCa5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>For a more controlled test, I turned to watching <em>Top Gun: Maverick</em> on our lab’s 4K Blu-ray player. As with <em>Project Hail Mary,</em> the movie looked surprisingly good in regular room lighting. The bright outdoor shots in the movie’s opening made the TV look better than its price — and brighter than my measured 520 nits in Professional Mode might show.</p><p>I noticed the fine details I never caught before: the drops of sweat on Tom Cruise’s hands as he worked on his jet, and as the bright sunlight shimmering on the fuselage on an airplane as the camera panned to it in the hangar. </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.15%;"><img id="HQgSXEBf8nHU5B3n7cpkf5" name="Sony-Bravia-3-II--6" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/HQgSXEBf8nHU5B3n7cpkf5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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, again, the lack of deep blacks that a Mini-LED or QLED TV might have are still present. As Tom Cruise got dressed and put on his pilot outfit, the blacks in the fabric looked slightly dull. Scenes still remained enjoyable though, just you will not get the reference-grade HDR brightness and accuracy you might get on the Bravia 9 II or more premium TVs.</p><p>Conversely, SDR performance is solid, especially with New York Mets MLB replays, and regular cartoons like <em>Spongebob Squarepants</em> on YouTube. </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.15%;"><img id="UV3N3soiXvouaf8E5yN9i5" name="Sony-Bravia-3-II--9" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/UV3N3soiXvouaf8E5yN9i5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>For SDR content, I watched about 20 minutes of a 45-minute-long <a href="https://www.youtube.com/watch?v=5VN6R4wox7E"><u>Pure Happiness at the Krusty Krab video.</u></a> On YouTube. Switching the Bravia 3 II to Vivid mode, I was blown away. It was my favorite way of testing the TV. </p><p>Spongebob’s skin popped with a bright vibrant yellow that I’ve never seen before, even on an OLED TV. The blues in a cabinet in the Krusty Krab’s kitchen, and the turquoise in Squidward's skin looked incredible. Even the red in Mr. Krabs’ shell jumped off the screen and appealed to my eyes right away.</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.15%;"><img id="ZWCnmnk8VptNDARP6KKhi5" name="Sony-Bravia-3-II--4" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/ZWCnmnk8VptNDARP6KKhi5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>This episode looked really bold and bright, and it was not what I expected out of this TV. Sony’s image processing really wins here, producing a more polished picture than I’ve seen from many budget TVs from Roku, Amazon and Vizio.</p><p>Of course, Vivid mode is designed to push brightness higher. It doesn’t go for the pure color accuracy which is where Professional mode is better. But the extra brightness really helps make cartoons, or animated series, and even sports look great on this TV.  </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.15%;"><img id="CQMEPcfbhoEGDYcHxKvFn5" name="Sony-Bravia-3-II--2" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/CQMEPcfbhoEGDYcHxKvFn5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I also switched the TV over to Standard mode and watched MLB highlights of a New York Mets game. The green grass of the MLB diamond looked alive. The “road gray” color in Kodai Senga’s Mets uniform was also great. I could make out the fine folds in his uniform as he threw out the final pitch to get his first career save. </p><p>Overall, there were a few snags with the black levels, but I was impressed with the Sony Bravia 3 II’s performance.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-test-results"><span>Sony Bravia 3 II review: Test results</span></h3><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p>Sony Bravia 3 II</p></td><td  ><p>Sony Bravia 3</p></td><td  ><p>Roku Select Series</p></td><td  ><p>Vizio Mini LED Quantum </p></td><td  ><p>Sony Bravia 7 II </p></td></tr><tr><td class="firstcol " ><p><strong>SDR Brightness (10%, in nits)</strong></p></td><td  ><p>352</p></td><td  ><p>397</p></td><td  ><p>334</p></td><td  ><p>218* </p></td><td  ><p>100</p></td></tr><tr><td class="firstcol " ><p><strong>Delta-E (lower is better)</strong></p></td><td  ><p>2.17</p></td><td  ><p>3.49</p></td><td  ><p>2.45</p></td><td  ><p>4.63 </p></td><td  ><p>4.79</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 709 Gamut Coverage</strong></p></td><td  ><p>98.81%</p></td><td  ><p>99.72%</p></td><td  ><p>93.85%</p></td><td  ><p>96.82%</p></td><td  ><p>99.94%</p></td></tr><tr><td class="firstcol " ><p><strong>HDR Brightness (10%, in nits)</strong></p></td><td  ><p>520</p></td><td  ><p>448</p></td><td  ><p>334</p></td><td  ><p>781  </p></td><td  ><p>2,077</p></td></tr><tr><td class="firstcol " ><p><strong>UHDA-P3 Gamut Coverage</strong></p></td><td  ><p>94.62%</p></td><td  ><p>95.88%</p></td><td  ><p>91.59%</p></td><td  ><p>92.94% </p></td><td  ><p>97.73%</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 2020 Gamut Coverage</strong></p></td><td  ><p>74.06%</p></td><td  ><p>69.81%</p></td><td  ><p>64.5%</p></td><td  ><p>71.37%</p></td><td  ><p>87.17%</p></td></tr><tr><td class="firstcol " ><p><strong>Input latency (in milliseconds)</strong></p></td><td  ><p>9.8</p></td><td  ><p>9.8</p></td><td  ><p>9.6</p></td><td  ><p>9.9</p></td><td  ><p>10.4</p></td></tr></tbody></table></div><p>As the lab results show, the 65-inch Bravia 3 II performs well for a direct-lit LED HDR TV. I measured 520 nits on a 10% window, which beats out the older 43-inch Bravia 3 (448 nits) — though that is a smaller size which impacts benchmarks. It also completely outperforms budget entry options like the Roku Select Series QLED (334 nits). </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.15%;"><img id="3NQGNw8UShFt9SsydrVfj5" name="Sony-Bravia-3-II--3" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/3NQGNw8UShFt9SsydrVfj5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>It, however, does not rival the Vizio Mini LED Quantum (781 nits), which has a different technology and the dimming zones this TV lacks. The flagship True RGB beast like the Bravia 7 II (2,077 nits) also won’t come close to it, but shows just how much more you can get for extra money.</p><p>Out of the box, the Bravia 3 II’s 2.12 Delta-E score in Professional Mode beats both the older Bravia 3 (3.49) and competing models like the Vizio Mini LED (4.63) This shows its strong color accuracy.</p><p>Not a lot of content is mastered for Rec 2020 coverage, but the Sony Bravia 3 II covers 74.06% of that gamut. That is a big jump from its smaller predecessor (69.81%). It is also ahead of the Roku QLED (64.50%), and the Vizio Mini LED (71.37%). For the more common UHDA-P3, it covers 94.62% of UHDA-P3, putting it third in the group, but these small percentage differences are not something your eyes will likely notice.  </p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-gaming"><span>Sony Bravia 3 II review: Gaming</span></h3><p>I was happy to see that the gaming experience on the Bravia 3 II’s game mode was smooth as butter — which is impressive, considering its sub-$1,000 price. Like the more expensive Bravia 7 II and Bravia 9 II, the TV supports Auto HDR Tone Mapping with PlayStation 5, along with VRR and ALLM support. This lets you skip the HDR optimization and auto applies the best settings for the TV’s HDR gaming features. </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.15%;"><img id="bx9cLPEDWZVJKpUfrYgkg5" name="Sony-Bravia-3-II--14" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/bx9cLPEDWZVJKpUfrYgkg5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Yet <em>unlike</em> the more expensive Bravia 7 II and Bravia 9 II, this TV has four HDMI 2.1 ports, meaning you can use up to four gaming consoles at 120Hz. For a gamer like me, that’s heaven. It is honestly very baffling to me why this TV has this many HDMI 2.1 ports and some of Sony's flagship TVs do not.</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.15%;"><img id="Wb6gUU9bz4bZseiji7x7k5" name="Sony-Bravia-3-II--13" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/Wb6gUU9bz4bZseiji7x7k5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>To test the gaming performance, I booted up <em>Pragamata</em> on our lab’s PlayStation 5. The game looked visually incredible, and played great. I experienced no lag in this game, where input timing is key. My quick inputs on the Triangle, Square, Circle buttons perfectly aligned with the hacking sequence needed to shoot enemy crusher robots in the Solar Power Plant sector.  </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.15%;"><img id="ZyvgxVkDRpUP2jdbrYCpi5" name="Sony-Bravia-3-II--11" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/ZyvgxVkDRpUP2jdbrYCpi5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Sony also includes the PS Remote Play app on the TV pre-installed. I have to admit, it was nice remoting into my own PS5 and playing a game like Gran Turismo 7 that we do not own on the lab’s console. </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.15%;"><img id="CgqzBjwNLDZ2wEKYEFfid5" name="Sony-Bravia-3-II--12" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/CgqzBjwNLDZ2wEKYEFfid5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>A dedicated Game Bar can also be summoned by pressing the “. . .” button on the remote when in the TV’s game mode. This gives you controls for VRR, Motion blur reduction, a Black equalizer, and the option to add a Crosshair. These are nice-to-have features at this TV’s price point.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-sound"><span>Sony Bravia 3 II review: Sound</span></h3><p>For audio testing, I watched the music videos for Post Malone’s “Sunflower” as well as <a href="https://www.youtube.com/watch?v=OgjzcwzIpyI"><u>Shaboozy’s “Highway”.</u></a> The Bravia 3 II sports "X-Balanced" speakers in a "Bass Reflex" enclosure. There's no subwoofers or surface drivers, so I would suggest pairing this TV with a soundbar if you plan to do anything beyond watching movies or TV shows.</p><p>On the first music video, I noticed how clear Post Malone’s vocals sounded. There was a lack of sub bass, but with the volume turned up to around 30%, I didn’t hear any distortion. </p><p>With Shaboozy’s music video, I heard the thump from his deeper voice, but the finer details from acoustic guitar was somewhat muted.</p><p>My recommendation? Check out our list of the <a href="https://www.tomsguide.com/audio/soundbars/best-soundbars"><u>best soundbars</u></a> to pair this set’s impressive performance with a more formidable piece of audio equipment.</p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-smart-tv-and-remote"><span>Sony Bravia 3 II review: Smart TV and remote</span></h3><p>We’ve reviewed almost a dozen TV this year with Google TV and the Bravia 3 II adds another tally to that list. Compared to what you'll have with a Roku TV or ad-ridden and privacy-last Vizio TV, Google TV is minimalist, clean and easy to use.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="DypMFjKQD8twXfKohCrNi5" name="Sony-Bravia-3-II--10" alt="A Sony Bravia 3 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/DypMFjKQD8twXfKohCrNi5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Everything you need is up front in a clean and easy to navigate experience, from settings, to inputs to TV shows and movies. The TV’s Google Gemini integration will have your back, too and will make finding what you’re looking for easy in a more human-friendly way. </p><p>Sony is recycling the remote from the Bravia 7 II on this year’s Bravia 3 II. It is a plastic remote, with a speckled design. It’s a bit big in the hands but still super comfortable to use. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="pDGMJLF9FMC2Q8wmWvw5o5" name="Sony-Bravia-3-II--22" alt="The remote for a Sony Bravia 3 II TV" src="https://cdn.mos.cms.futurecdn.net/pDGMJLF9FMC2Q8wmWvw5o5-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>There’s the usual suite of buttons for navigation, apps like Disney+, Netflix, YouTube, voice control, and settings and menu. The remote can also be found using the power button on the TV. You also can program your own app to the star button on the remote. </p><h3 class="article-body__section" id="section-sony-bravia-3-ii-review-verdict"><span>Sony Bravia 3 II review: Verdict</span></h3><p>The Sony Bravia 3 II is a surprising TV. It brings Sony’s flagship-level image processing to a basic LED panel to help make your favorite TV shows and movies look great when the lights are on. It might not match Mini-LED or QLED TVs for brightness or black levels, but it’s still great for the everyday TV viewer. </p><p>The Bravia 3 II will be a solid purchase thanks to its gaming performance. It is great for gamers with its four HDMI 2.1 ports, its sleek Google TV interface, and a smart design. If you're buying a 65-inch TV primarily for daytime living-room viewing and gaming, this is the one for you. </p><p>If you want cheaper, sub-$500 options, the aforementioned Roku or Vizio models are good first ports of call, while the more-expensive Bravia 7 II will deliver better color and brightness. As a jack-of-all-trades, though, the Bravia 3 II is a good all-arounder.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Sony just refreshed three of its most popular headphones with replaceable batteries and punchier sound — say hello to the XM4C, CH730N and CH530 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Sony announced three new pairs of headphones — well, new-ish. They're the upgraded versions, or refreshes, of their predecessors. After having sold 10 million units globally, the <a href="https://www.tomsguide.com/reviews/sony-wh-1000xm4">Sony WH-1000XM4</a> is being succeeded by the Sony WH-1000XM4C. And there's more: the Sony WH-CH730N is here to replace the three-year-old Sony WH-CH720N, and the Sony WH-CH530 now succeeds the budget-friendly <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-ch520-review">Sony WH-CH520</a>.</p><p>If that sounds like a lot to take in, don't worry: I'm here to break it down for you. The XM4C announcement comes hot on the heels of Sony celebrating 10 years of the WH-1000X series. That's right: the first model, the MDR-1000X, came out in 2016. Along with that milestone, Sony says it is also celebrating 80 years of audio innovation.</p><p>So, what's new, what's changed (and hasn't), how much do they cost, and should you buy them? Let me walk you through it.</p><h2 id="wh-1000xm4c-39-a-modern-classic-39">WH-1000XM4C: 'A modern classic'</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:3713px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="yUuWgfyvzYcDqLfJGfG6Ln" name="Sony-new-headphones-8" alt="Sony WH-1000XM4C headphones" src="https://cdn.mos.cms.futurecdn.net/yUuWgfyvzYcDqLfJGfG6Ln-1920-80.jpg" mos="" align="middle" fullscreen="" width="3713" height="2089" 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>When the Sony WH-1000XM4 came out in 2020, they were an instant hit, and Sony says that the headphones that redefined its cans have sold over 10 million units globally. It's no wonder they're back in the form of the WH-1000XM4C — and they sound like a hoot. While the overall design and active noise cancellation remain unchanged, Sony is introducing <strong>a new lavender color,</strong> which looks gorgeous.</p><p>So what's new? Upgraded sound tuning thanks to <strong>40mm drivers and Bluetooth 6.0</strong>, for starters. The XM4C inherits the XM6's and the <a href="https://www.tomsguide.com/audio/headphones/i-tested-sonys-usd650-fashion-headphones-the-wh-1000x-the-collexion-are-more-than-just-pretty">1000X The Collexion</a>'s tuning to deliver a versatile sound that outperforms the original XM4. Vocals are now richer and clearer, and the XM4C are meant to boast superior frequency separation and an expansive soundstage, alongside improved dynamics.</p><p>They also include a <strong>10-band equalizer, gaming EQ, and Quick Access</strong>, which lets you launch your preferred <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">music streaming service</a> with headband gestures.</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:3064px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="fAqNFKfw5UzT2aBf79znhm" name="Sony-new-headphones-4" alt="Sony WH-1000XM4C headphones" src="https://cdn.mos.cms.futurecdn.net/fAqNFKfw5UzT2aBf79znhm-1920-80.jpg" mos="" align="middle" fullscreen="" width="3064" height="1723" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The biggest upgrade is <strong>user-replaceable batteries</strong>, keeping in line with the <a href="https://www.tomsguide.com/phones/iphones/eu-is-mandating-readily-removable-batteries-for-phones-but-iphones-may-be-exempt">new EU battery regulations</a>. This comes at a cost, though: the XM4C's battery life is rated at 27 hours, down from 30 hours on the original XM4.</p><p>For those wondering where the XM4C sits relative to Sony's other headphones, here's the answer. According to Sony, the 1000X The Collexion are "Luxury"; the XM6 are "Performance"; and the XM4C are "Classic Standard." They're designed for everyday use without sacrificing sound quality.</p><p><em>The Sony WH-1000XM4C are available for </em><a href="https://electronics.sony.com/c/all-headphones" target="_blank" rel="nofollow"><em>$299 / ~£220</em></a><em>.</em></p><h2 id="wh-ch730n-now-with-75-hours-of-battery-life">WH-CH730N: Now with 75 hours of battery life</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:3749px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="bDWPMstToQJVTUNTqrc7Ln" name="Sony-new-headphones-7" alt="Sony WH-CH730N headphones" src="https://cdn.mos.cms.futurecdn.net/bDWPMstToQJVTUNTqrc7Ln-1920-80.jpg" mos="" align="middle" fullscreen="" width="3749" height="2109" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony WH-CH720N came out in 2023, and they're already getting refreshed — and the new WH-CH730N packs some big upgrades. Like the XM4C, the design stays mostly the same, but the CH730N now has a <strong>foldable design</strong> for easy transport, plus a cushioned headband.</p><p>They also feature stronger ANC and improved AI-powered call quality. They also include a <strong>10-band customizable equalizer</strong>, <strong>DSEE music upscaling,</strong> and <strong>support for Sony's 360° Reality Audio</strong>. The battery is, again, replaceable, but unlike the XM4C, battery life has received a substantial bump. It's now up to <strong>75 hours</strong>, up from 50 hours on its predecessor. Charging the CH730N for three minutes also yields an hour of playtime.</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:3865px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="i5wDNxLeUAFpanfSnaGQEn" name="Sony-new-headphones" alt="Sony WH-CH730N headphones" src="https://cdn.mos.cms.futurecdn.net/i5wDNxLeUAFpanfSnaGQEn-1920-80.jpg" mos="" align="middle" fullscreen="" width="3865" height="2174" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The CH730N utilizes <strong>Bluetooth 6.0 with Bluetooth LE and Auracast</strong>. Another new feature is Max Volume Control, which you can use in the app to cap the maximum volume and protect your hearing. In addition to black, blue, pink, and white, the CH730N now comes in beige as well.</p><p><em>The Sony WH-CH730N is available for </em><a href="https://electronics.sony.com/c/all-headphones" target="_blank" rel="nofollow"><em>$179 / ~£120</em></a><em>.</em></p><h2 id="wh-ch530-the-new-budget-king">WH-CH530: The new budget king?</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:3737px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5fBG3cYGhYaEie6FVGhFwm" name="Sony-new-headphones-6" alt="Sony WH-CH530 headphones" src="https://cdn.mos.cms.futurecdn.net/5fBG3cYGhYaEie6FVGhFwm-1920-80.jpg" mos="" align="middle" fullscreen="" width="3737" height="2102" 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>When we <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-ch520-review">reviewed the Sony WH-CH520</a>, we awarded them 4.5-stars and noted they were fantastic value for money. We expect the new WH-CH530 to be just as good, if not better.</p><p>Out of the three refreshed models, the CH530 has received the fewest upgrades, but substantial ones nonetheless. They now come in <strong>coral (off-red/orange) and cobalt</strong>, as well as mint, pink, off-white, and black.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3626px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="pWCM94UuZuZKJwv2mHy2Fn" name="Sony-new-headphones-2" alt="Sony WH-CH530 headphones" src="https://cdn.mos.cms.futurecdn.net/pWCM94UuZuZKJwv2mHy2Fn-1920-80.jpg" mos="" align="middle" fullscreen="" width="3626" height="2040" 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>Battery life has received a five-hour bump, and the CH530 is rated for <strong>55 hours</strong> (up from 50 hours). Three minutes of charge yields 90 minutes of playtime, which is excellent. Similar to the CH730N, the CH530 also gets <strong>Max Volume Control, Bluetooth LE audio, replaceable batteries, and AI noise reduction</strong> for calls.</p><p><em>The Sony WH-CH530 is available for </em><a href="https://electronics.sony.com/c/all-headphones" target="_blank" rel="nofollow"><em>$69 / ~£60</em></a><em>.</em></p><h2 id="which-headphones-are-you-most-excited-for">Which headphones are you most excited for?</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:3674px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="HSbqz9xDw9h24dG29iWmFn" name="Sony-new-headphones-5" alt="Sony WH-CH530 headphones" src="https://cdn.mos.cms.futurecdn.net/HSbqz9xDw9h24dG29iWmFn-1920-80.jpg" mos="" align="middle" fullscreen="" width="3674" height="2067" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As someone who reviews the <a href="https://www.tomsguide.com/best-picks/best-over-ear-headphones">best headphones</a> day in and day out and spends a lot of her time listening to music, it goes without saying that I'm very excited to try Sony's new cans. The XM4C, CH730N, and CH530 introduce notable quality-of-life improvements, including replaceable batteries and better sound tuning.</p><p>It remains to be seen whether these headphones impress me and my fellow reviewers, so we can't wait to get our hands on them. Personally, I'm most excited to check out the XM4C, especially in the lavender skin. Who knows, maybe they'll replace my trusty <a href="https://www.tomsguide.com/audio/headphones/my-doctor-banned-me-from-earbuds-so-i-got-these-marshall-headphones-instead-and-im-surprised-i-didnt-do-it-sooner">Marshall Monitor III</a>? Stay tuned for full reviews!</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="http://www.tomsguide.com/audio/bluetooth-speakers/sony-ult-tower-party-speakers-hands-on">I listened to 10 of Sony’s new ULT party speakers together — and they offer something for everyone</a></li><li><a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-3-months-later-heres-my-honest-verdict-on-how-sonys-flagship-cans-hold-up">Sony WH-1000XM6 3 months later: here's my honest verdict on how Sony's flagship cans hold up</a></li><li><a href="https://www.tomsguide.com/audio/headphones/i-tested-sonys-usd650-fashion-headphones-the-wh-1000x-the-collexion-are-more-than-just-pretty">I tested Sony’s $650 fashion headphones — the 1000X 'The Collexion' are more than just pretty</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/headphones/sony-wh-1000xm4c-wh-ch730n-wh-ch530-announced</link>
                                                                            <description>
                            <![CDATA[ The new Sony WH-1000XM4C, WH-CH730N, and WH-CH530 now feature replaceable batteries, improved sound tuning and dynamics, and come in fresh colors. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">r2eM8TfpwZZpTsTc9JCrzk</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/dTQ9wHNii9UL29wqs3hEMn-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 07 Sep 2026 16:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Headphones]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3-320-70.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres.&lt;/p&gt;&lt;p&gt;A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&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/dTQ9wHNii9UL29wqs3hEMn-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sony WH-1000XM4C headphones]]></media:description>                                                            <media:text><![CDATA[Sony WH-1000XM4C headphones]]></media:text>
                                <media:title type="plain"><![CDATA[Sony WH-1000XM4C headphones]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/dTQ9wHNii9UL29wqs3hEMn-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Sony announced three new pairs of headphones — well, new-ish. They're the upgraded versions, or refreshes, of their predecessors. After having sold 10 million units globally, the <a href="https://www.tomsguide.com/reviews/sony-wh-1000xm4">Sony WH-1000XM4</a> is being succeeded by the Sony WH-1000XM4C. And there's more: the Sony WH-CH730N is here to replace the three-year-old Sony WH-CH720N, and the Sony WH-CH530 now succeeds the budget-friendly <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-ch520-review">Sony WH-CH520</a>.</p><p>If that sounds like a lot to take in, don't worry: I'm here to break it down for you. The XM4C announcement comes hot on the heels of Sony celebrating 10 years of the WH-1000X series. That's right: the first model, the MDR-1000X, came out in 2016. Along with that milestone, Sony says it is also celebrating 80 years of audio innovation.</p><p>So, what's new, what's changed (and hasn't), how much do they cost, and should you buy them? Let me walk you through it.</p><h2 id="wh-1000xm4c-39-a-modern-classic-39">WH-1000XM4C: 'A modern classic'</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:3713px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="yUuWgfyvzYcDqLfJGfG6Ln" name="Sony-new-headphones-8" alt="Sony WH-1000XM4C headphones" src="https://cdn.mos.cms.futurecdn.net/yUuWgfyvzYcDqLfJGfG6Ln-1920-80.jpg" mos="" align="middle" fullscreen="" width="3713" height="2089" 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>When the Sony WH-1000XM4 came out in 2020, they were an instant hit, and Sony says that the headphones that redefined its cans have sold over 10 million units globally. It's no wonder they're back in the form of the WH-1000XM4C — and they sound like a hoot. While the overall design and active noise cancellation remain unchanged, Sony is introducing <strong>a new lavender color,</strong> which looks gorgeous.</p><p>So what's new? Upgraded sound tuning thanks to <strong>40mm drivers and Bluetooth 6.0</strong>, for starters. The XM4C inherits the XM6's and the <a href="https://www.tomsguide.com/audio/headphones/i-tested-sonys-usd650-fashion-headphones-the-wh-1000x-the-collexion-are-more-than-just-pretty">1000X The Collexion</a>'s tuning to deliver a versatile sound that outperforms the original XM4. Vocals are now richer and clearer, and the XM4C are meant to boast superior frequency separation and an expansive soundstage, alongside improved dynamics.</p><p>They also include a <strong>10-band equalizer, gaming EQ, and Quick Access</strong>, which lets you launch your preferred <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">music streaming service</a> with headband gestures.</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:3064px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="fAqNFKfw5UzT2aBf79znhm" name="Sony-new-headphones-4" alt="Sony WH-1000XM4C headphones" src="https://cdn.mos.cms.futurecdn.net/fAqNFKfw5UzT2aBf79znhm-1920-80.jpg" mos="" align="middle" fullscreen="" width="3064" height="1723" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The biggest upgrade is <strong>user-replaceable batteries</strong>, keeping in line with the <a href="https://www.tomsguide.com/phones/iphones/eu-is-mandating-readily-removable-batteries-for-phones-but-iphones-may-be-exempt">new EU battery regulations</a>. This comes at a cost, though: the XM4C's battery life is rated at 27 hours, down from 30 hours on the original XM4.</p><p>For those wondering where the XM4C sits relative to Sony's other headphones, here's the answer. According to Sony, the 1000X The Collexion are "Luxury"; the XM6 are "Performance"; and the XM4C are "Classic Standard." They're designed for everyday use without sacrificing sound quality.</p><p><em>The Sony WH-1000XM4C are available for </em><a href="https://electronics.sony.com/c/all-headphones" target="_blank" rel="nofollow"><em>$299 / ~£220</em></a><em>.</em></p><h2 id="wh-ch730n-now-with-75-hours-of-battery-life">WH-CH730N: Now with 75 hours of battery life</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:3749px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="bDWPMstToQJVTUNTqrc7Ln" name="Sony-new-headphones-7" alt="Sony WH-CH730N headphones" src="https://cdn.mos.cms.futurecdn.net/bDWPMstToQJVTUNTqrc7Ln-1920-80.jpg" mos="" align="middle" fullscreen="" width="3749" height="2109" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony WH-CH720N came out in 2023, and they're already getting refreshed — and the new WH-CH730N packs some big upgrades. Like the XM4C, the design stays mostly the same, but the CH730N now has a <strong>foldable design</strong> for easy transport, plus a cushioned headband.</p><p>They also feature stronger ANC and improved AI-powered call quality. They also include a <strong>10-band customizable equalizer</strong>, <strong>DSEE music upscaling,</strong> and <strong>support for Sony's 360° Reality Audio</strong>. The battery is, again, replaceable, but unlike the XM4C, battery life has received a substantial bump. It's now up to <strong>75 hours</strong>, up from 50 hours on its predecessor. Charging the CH730N for three minutes also yields an hour of playtime.</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:3865px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="i5wDNxLeUAFpanfSnaGQEn" name="Sony-new-headphones" alt="Sony WH-CH730N headphones" src="https://cdn.mos.cms.futurecdn.net/i5wDNxLeUAFpanfSnaGQEn-1920-80.jpg" mos="" align="middle" fullscreen="" width="3865" height="2174" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The CH730N utilizes <strong>Bluetooth 6.0 with Bluetooth LE and Auracast</strong>. Another new feature is Max Volume Control, which you can use in the app to cap the maximum volume and protect your hearing. In addition to black, blue, pink, and white, the CH730N now comes in beige as well.</p><p><em>The Sony WH-CH730N is available for </em><a href="https://electronics.sony.com/c/all-headphones" target="_blank" rel="nofollow"><em>$179 / ~£120</em></a><em>.</em></p><h2 id="wh-ch530-the-new-budget-king">WH-CH530: The new budget king?</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:3737px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5fBG3cYGhYaEie6FVGhFwm" name="Sony-new-headphones-6" alt="Sony WH-CH530 headphones" src="https://cdn.mos.cms.futurecdn.net/5fBG3cYGhYaEie6FVGhFwm-1920-80.jpg" mos="" align="middle" fullscreen="" width="3737" height="2102" 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>When we <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-ch520-review">reviewed the Sony WH-CH520</a>, we awarded them 4.5-stars and noted they were fantastic value for money. We expect the new WH-CH530 to be just as good, if not better.</p><p>Out of the three refreshed models, the CH530 has received the fewest upgrades, but substantial ones nonetheless. They now come in <strong>coral (off-red/orange) and cobalt</strong>, as well as mint, pink, off-white, and black.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3626px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="pWCM94UuZuZKJwv2mHy2Fn" name="Sony-new-headphones-2" alt="Sony WH-CH530 headphones" src="https://cdn.mos.cms.futurecdn.net/pWCM94UuZuZKJwv2mHy2Fn-1920-80.jpg" mos="" align="middle" fullscreen="" width="3626" height="2040" 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>Battery life has received a five-hour bump, and the CH530 is rated for <strong>55 hours</strong> (up from 50 hours). Three minutes of charge yields 90 minutes of playtime, which is excellent. Similar to the CH730N, the CH530 also gets <strong>Max Volume Control, Bluetooth LE audio, replaceable batteries, and AI noise reduction</strong> for calls.</p><p><em>The Sony WH-CH530 is available for </em><a href="https://electronics.sony.com/c/all-headphones" target="_blank" rel="nofollow"><em>$69 / ~£60</em></a><em>.</em></p><h2 id="which-headphones-are-you-most-excited-for">Which headphones are you most excited for?</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:3674px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="HSbqz9xDw9h24dG29iWmFn" name="Sony-new-headphones-5" alt="Sony WH-CH530 headphones" src="https://cdn.mos.cms.futurecdn.net/HSbqz9xDw9h24dG29iWmFn-1920-80.jpg" mos="" align="middle" fullscreen="" width="3674" height="2067" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As someone who reviews the <a href="https://www.tomsguide.com/best-picks/best-over-ear-headphones">best headphones</a> day in and day out and spends a lot of her time listening to music, it goes without saying that I'm very excited to try Sony's new cans. The XM4C, CH730N, and CH530 introduce notable quality-of-life improvements, including replaceable batteries and better sound tuning.</p><p>It remains to be seen whether these headphones impress me and my fellow reviewers, so we can't wait to get our hands on them. Personally, I'm most excited to check out the XM4C, especially in the lavender skin. Who knows, maybe they'll replace my trusty <a href="https://www.tomsguide.com/audio/headphones/my-doctor-banned-me-from-earbuds-so-i-got-these-marshall-headphones-instead-and-im-surprised-i-didnt-do-it-sooner">Marshall Monitor III</a>? Stay tuned for full reviews!</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="http://www.tomsguide.com/audio/bluetooth-speakers/sony-ult-tower-party-speakers-hands-on">I listened to 10 of Sony’s new ULT party speakers together — and they offer something for everyone</a></li><li><a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-3-months-later-heres-my-honest-verdict-on-how-sonys-flagship-cans-hold-up">Sony WH-1000XM6 3 months later: here's my honest verdict on how Sony's flagship cans hold up</a></li><li><a href="https://www.tomsguide.com/audio/headphones/i-tested-sonys-usd650-fashion-headphones-the-wh-1000x-the-collexion-are-more-than-just-pretty">I tested Sony’s $650 fashion headphones — the 1000X 'The Collexion' are more than just pretty</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Sony, Canon, and Nikon camera prices slashed in Best Buy’s Labor Day sale– up to 20% off ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Labor Day weekend is almost upon us — and with it comes the sales! All of the big retailers have a huge range of time-sensitive deals right now, so if you're planning to make a big purchase, now is a great time.</p><p>Best Buy currently has tons of deals available on cameras. To save you the trouble of browsing through the hundreds of offers available, I've pulled out 12 of the best camera deals on the site, from huge brands like Nikon, Canon, and Sony.</p><p>As we always like to remind you, deals don't last forever, so if there's something that you have your eye on here make sure to grab it while it's still available.</p>        <div class="featured_product_block featured_block_hero" data-id="40846f02-a7b1-11f1-af28-2327be420d02">            <a href="https://www.bestbuy.com/product/canon-eos-rebel-t7-dslr-camera-w-18-55mm-lens-starter-bundle-w-eos-bag-ultra-64gb-black/J7QPQK63ZQ/sku/11834823" data-model-name="EOS Rebel T7 DSLR and 18-55mm Lens Bundle" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/zQtuxeAs27VFaShuETnWAS.jpg" alt="BRAND, Bundle - Canon - EOS Rebel T7 DSLR Camera w 18-55mm Lens Starter + w: EOS Bag + Ultra 64GB - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">EOS Rebel T7 DSLR and 18-55mm Lens Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40846f70-a7b1-11f1-bc9f-418aea3fa7aa">            <a href="https://www.bestbuy.com/product/canon-eos-rebel-t7-dslr-video-two-lens-kit-with-ef-s-18-55mm-and-ef-75-300mm-lenses-black/JXL8XXCR6G/sku/6323759" data-model-name="EOS Rebel T7 DSLR with Two Lens Bundle" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jACd2QXsEJNyf5GpAQnrsT.jpg" alt="Canon, Canon - Eos Rebel T7 Dslr Video Two Lens Kit With Ef-S 18-55mm and Ef 75-300mm Lenses - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">EOS Rebel T7 DSLR with Two Lens Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40846fd4-a7b1-11f1-a073-4d1f0705125a">            <a href="https://www.bestbuy.com/product/canon-eos-r50-v-4k-video-mirrorless-camera-with-rf-s14-30mm-f4-6-3-is-stm-pz-lens-black/J7C86SHSSV/sku/6624075" data-model-name="EOS R50 V Mirrorless Camera with RF-S14-30mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/t89urxBrtdo82NrHomf3MV.jpg" alt="BRAND, Canon - EOS R50 V 4K Video Mirrorless Camera with RF-S14-30mm F4-6.3 IS STM PZ Lens - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">EOS R50 V Mirrorless Camera with RF-S14-30mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40847038-a7b1-11f1-9b7e-1d95295843fb">            <a href="https://www.bestbuy.com/product/sony-alpha-7-iv-full-frame-mirrorless-kit-with-sel28702-lens-black/J7XSRH5ZPY/sku/6670597" data-model-name="Alpha 7 IV Full-Frame Mirrorless with SEL28702 Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Ep8NddAJdyQb2XBiSXTB8X.jpg" alt="BRAND, Sony - Alpha 7 IV Full-frame Mirrorless Kit with SEL28702 Lens - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Alpha 7 IV Full-Frame Mirrorless with SEL28702 Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="408470a6-a7b1-11f1-b6b5-a3812e17be52">            <a href="https://www.bestbuy.com/product/sony-a6400-mirrorless-camera-ilce-6400m-b-18-135mm-lens-kit-deco-gear-case-bundle-black/J36XL3FP87/sku/12360748" data-model-name="A6400 Mirrorless Camera with 18-135mm Lens Bundle" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/UKcXrPdN4i7xQxREzV2LSk.jpg" alt="Bundle - Sony - A6400 Mirrorless Camera Ilce-6400m/b 18-135mm Lens Kit + Deco Gear Case - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">A6400 Mirrorless Camera with 18-135mm Lens Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40847100-a7b1-11f1-9032-b92e19fce459">            <a href="https://www.bestbuy.com/product/sony-alpha-6400-aps-c-mirrorless-camera-4k-video-camera-with-e-pz-16-50mm-lens-black/J7XSRH5HV9/sku/6615139" data-model-name="Alpha 6400 APS-C Mirrorless Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/esUtbq4suuZBL5jQF6JHEo.jpg" alt="Sony, Sony - Alpha 6400 Aps-C Mirrorless Camera 4k Video Camera With E Pz 16-50mm Lens - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Alpha 6400 APS-C Mirrorless Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4084715a-a7b1-11f1-a0fa-67a99d0911e6">            <a href="https://www.bestbuy.com/product/sony-alpha-zv-e10-ii-mirrorless-content-creators-camera-kit-with-16-50mm-oss-ii-lens-black/J7XSRH5689/sku/6589380" data-model-name="Alpha ZV-E10 II Mirrorless Content Creators' Camera Kit" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/bAsXJ6SGueEiVodU4SGDw.jpg" alt="Sony, Sony - Alpha Zv-E10 Ii Mirrorless Content Creators’ Camera Kit With 16-50mm Oss Ii Lens - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Alpha ZV-E10 II Mirrorless Content Creators' Camera Kit</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="408471b4-a7b1-11f1-bada-492e1510e818">            <a href="https://www.bestbuy.com/product/canon-powershot-elph-360-hs-a-full-hd-video-20-2-megapixel-digital-camera-silver/J7C86SHZLS/sku/12657481" data-model-name="Powershot ELPH 360 20.2-Megapixel Digital Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/eY7YrmTdZQ9iz98GHodFC4.jpg" alt="Canon, Canon - Powershot Elph 360 Hs a Full Hd Video 20.2-Megapixel Digital Camera - Silver"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">Powershot ELPH 360 20.2-Megapixel Digital Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40847218-a7b1-11f1-bf62-d5848454934b">            <a href="https://www.bestbuy.com/product/nikon-z6iii-6k-video-mirrorless-camera-body-black/J7CVR9638J/sku/12635820" data-model-name="Z6III Mirrorless Camera (body only)" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/xAuxixdjzkwDRyvTps6hk5.jpg" alt="Nikon, Nikon - Z6iii 6k Video Mirrorless Camera Body - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nikon</div>                                        <div class="featured__title">Z6III Mirrorless Camera (body only)</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40847272-a7b1-11f1-b9bd-c116cdacf96b">            <a href="https://www.bestbuy.com/product/nikon-z-8-8k-video-mirrorless-camera-body-only-black/J7CVR96C2C/sku/12635870" data-model-name="Z 8 Mirrorless Camera (body only)" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/kGQZUVka9dCJj9Jus8EHJ7.jpg" alt="BRAND, Nikon - Z 8 8K Video Mirrorless Camera (Body Only) - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nikon</div>                                        <div class="featured__title">Z 8 Mirrorless Camera (body only)</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="408472cc-a7b1-11f1-85e3-b14f3b2560e9">            <a href="https://www.bestbuy.com/product/panasonic-lumix-g100d-mirrorless-camera-for-photo-4k-video-and-vlogging-12-32mm-lens-black/J3Z7HSLW6C/sku/12808980" data-model-name="LUMIX G100D Mirrorless with 12-32mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Fe2MNbF7NW3VGQ3t56vuc9.jpg" alt="BRAND, Panasonic - LUMIX G100D Mirrorless Camera for Photo, 4K Video and Vlogging, 12-32mm Lens - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Panasonic</div>                                        <div class="featured__title">LUMIX G100D Mirrorless with 12-32mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40847326-a7b1-11f1-97fe-f7165cbd8ea4">            <a href="https://www.bestbuy.com/product/canon-eos-r7-mirrorless-camera-with-18-150mm-lens-5137c009/JXVLXKXGCK/sku/12831670" data-model-name="EOS R7 Mirrorless Camera with 18-150mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/8NfML8ECs8qnx7cmVoCYuA.jpg" alt="BRAND, Canon - EOS R7 Mirrorless Camera with 18-150mm Lens - 5137C009"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>BRAND</div>                                        <div class="featured__title">EOS R7 Mirrorless Camera with 18-150mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/sales-events/sony-canon-and-nikon-camera-prices-slashed-in-best-buys-labor-day-sale-up-to-20-percent-off</link>
                                                                            <description>
                            <![CDATA[ Best Buy's massive Labor Day sale continues, with some huge savings available on a few range of cameras, from content creator kits to high-end DSLRs. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">6QPyB9wLAewv6NTatFyWVD</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/VDQErWM9V8uk3aQG9XyM9G-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 04 Sep 2026 16:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Sales Events]]></category>
                                                                                                                    <dc:creator><![CDATA[ Will Salmon ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/HpRWP7Mh9ErgRGoTvZBbBJ-320-70.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/VDQErWM9V8uk3aQG9XyM9G-1920-80.jpg">
                                                            <media:credit><![CDATA[Canon, Sony, Nikon, Lumix / Edited with Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of cameras.]]></media:description>                                                            <media:text><![CDATA[A selection of cameras.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of cameras.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/VDQErWM9V8uk3aQG9XyM9G-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Labor Day weekend is almost upon us — and with it comes the sales! All of the big retailers have a huge range of time-sensitive deals right now, so if you're planning to make a big purchase, now is a great time.</p><p>Best Buy currently has tons of deals available on cameras. To save you the trouble of browsing through the hundreds of offers available, I've pulled out 12 of the best camera deals on the site, from huge brands like Nikon, Canon, and Sony.</p><p>As we always like to remind you, deals don't last forever, so if there's something that you have your eye on here make sure to grab it while it's still available.</p>        <div class="featured_product_block featured_block_hero" data-id="40846f02-a7b1-11f1-af28-2327be420d02">            <a href="https://www.bestbuy.com/product/canon-eos-rebel-t7-dslr-camera-w-18-55mm-lens-starter-bundle-w-eos-bag-ultra-64gb-black/J7QPQK63ZQ/sku/11834823" data-model-name="EOS Rebel T7 DSLR and 18-55mm Lens Bundle" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/zQtuxeAs27VFaShuETnWAS.jpg" alt="BRAND, Bundle - Canon - EOS Rebel T7 DSLR Camera w 18-55mm Lens Starter + w: EOS Bag + Ultra 64GB - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">EOS Rebel T7 DSLR and 18-55mm Lens Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40846f70-a7b1-11f1-bc9f-418aea3fa7aa">            <a href="https://www.bestbuy.com/product/canon-eos-rebel-t7-dslr-video-two-lens-kit-with-ef-s-18-55mm-and-ef-75-300mm-lenses-black/JXL8XXCR6G/sku/6323759" data-model-name="EOS Rebel T7 DSLR with Two Lens Bundle" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jACd2QXsEJNyf5GpAQnrsT.jpg" alt="Canon, Canon - Eos Rebel T7 Dslr Video Two Lens Kit With Ef-S 18-55mm and Ef 75-300mm Lenses - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">EOS Rebel T7 DSLR with Two Lens Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40846fd4-a7b1-11f1-a073-4d1f0705125a">            <a href="https://www.bestbuy.com/product/canon-eos-r50-v-4k-video-mirrorless-camera-with-rf-s14-30mm-f4-6-3-is-stm-pz-lens-black/J7C86SHSSV/sku/6624075" data-model-name="EOS R50 V Mirrorless Camera with RF-S14-30mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/t89urxBrtdo82NrHomf3MV.jpg" alt="BRAND, Canon - EOS R50 V 4K Video Mirrorless Camera with RF-S14-30mm F4-6.3 IS STM PZ Lens - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">EOS R50 V Mirrorless Camera with RF-S14-30mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40847038-a7b1-11f1-9b7e-1d95295843fb">            <a href="https://www.bestbuy.com/product/sony-alpha-7-iv-full-frame-mirrorless-kit-with-sel28702-lens-black/J7XSRH5ZPY/sku/6670597" data-model-name="Alpha 7 IV Full-Frame Mirrorless with SEL28702 Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Ep8NddAJdyQb2XBiSXTB8X.jpg" alt="BRAND, Sony - Alpha 7 IV Full-frame Mirrorless Kit with SEL28702 Lens - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Alpha 7 IV Full-Frame Mirrorless with SEL28702 Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="408470a6-a7b1-11f1-b6b5-a3812e17be52">            <a href="https://www.bestbuy.com/product/sony-a6400-mirrorless-camera-ilce-6400m-b-18-135mm-lens-kit-deco-gear-case-bundle-black/J36XL3FP87/sku/12360748" data-model-name="A6400 Mirrorless Camera with 18-135mm Lens Bundle" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/UKcXrPdN4i7xQxREzV2LSk.jpg" alt="Bundle - Sony - A6400 Mirrorless Camera Ilce-6400m/b 18-135mm Lens Kit + Deco Gear Case - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">A6400 Mirrorless Camera with 18-135mm Lens Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40847100-a7b1-11f1-9032-b92e19fce459">            <a href="https://www.bestbuy.com/product/sony-alpha-6400-aps-c-mirrorless-camera-4k-video-camera-with-e-pz-16-50mm-lens-black/J7XSRH5HV9/sku/6615139" data-model-name="Alpha 6400 APS-C Mirrorless Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/esUtbq4suuZBL5jQF6JHEo.jpg" alt="Sony, Sony - Alpha 6400 Aps-C Mirrorless Camera 4k Video Camera With E Pz 16-50mm Lens - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Alpha 6400 APS-C Mirrorless Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="4084715a-a7b1-11f1-a0fa-67a99d0911e6">            <a href="https://www.bestbuy.com/product/sony-alpha-zv-e10-ii-mirrorless-content-creators-camera-kit-with-16-50mm-oss-ii-lens-black/J7XSRH5689/sku/6589380" data-model-name="Alpha ZV-E10 II Mirrorless Content Creators' Camera Kit" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/bAsXJ6SGueEiVodU4SGDw.jpg" alt="Sony, Sony - Alpha Zv-E10 Ii Mirrorless Content Creators’ Camera Kit With 16-50mm Oss Ii Lens - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Alpha ZV-E10 II Mirrorless Content Creators' Camera Kit</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="408471b4-a7b1-11f1-bada-492e1510e818">            <a href="https://www.bestbuy.com/product/canon-powershot-elph-360-hs-a-full-hd-video-20-2-megapixel-digital-camera-silver/J7C86SHZLS/sku/12657481" data-model-name="Powershot ELPH 360 20.2-Megapixel Digital Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/eY7YrmTdZQ9iz98GHodFC4.jpg" alt="Canon, Canon - Powershot Elph 360 Hs a Full Hd Video 20.2-Megapixel Digital Camera - Silver"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">Powershot ELPH 360 20.2-Megapixel Digital Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40847218-a7b1-11f1-bf62-d5848454934b">            <a href="https://www.bestbuy.com/product/nikon-z6iii-6k-video-mirrorless-camera-body-black/J7CVR9638J/sku/12635820" data-model-name="Z6III Mirrorless Camera (body only)" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/xAuxixdjzkwDRyvTps6hk5.jpg" alt="Nikon, Nikon - Z6iii 6k Video Mirrorless Camera Body - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nikon</div>                                        <div class="featured__title">Z6III Mirrorless Camera (body only)</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40847272-a7b1-11f1-b9bd-c116cdacf96b">            <a href="https://www.bestbuy.com/product/nikon-z-8-8k-video-mirrorless-camera-body-only-black/J7CVR96C2C/sku/12635870" data-model-name="Z 8 Mirrorless Camera (body only)" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/kGQZUVka9dCJj9Jus8EHJ7.jpg" alt="BRAND, Nikon - Z 8 8K Video Mirrorless Camera (Body Only) - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nikon</div>                                        <div class="featured__title">Z 8 Mirrorless Camera (body only)</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="408472cc-a7b1-11f1-85e3-b14f3b2560e9">            <a href="https://www.bestbuy.com/product/panasonic-lumix-g100d-mirrorless-camera-for-photo-4k-video-and-vlogging-12-32mm-lens-black/J3Z7HSLW6C/sku/12808980" data-model-name="LUMIX G100D Mirrorless with 12-32mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Fe2MNbF7NW3VGQ3t56vuc9.jpg" alt="BRAND, Panasonic - LUMIX G100D Mirrorless Camera for Photo, 4K Video and Vlogging, 12-32mm Lens - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Panasonic</div>                                        <div class="featured__title">LUMIX G100D Mirrorless with 12-32mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40847326-a7b1-11f1-97fe-f7165cbd8ea4">            <a href="https://www.bestbuy.com/product/canon-eos-r7-mirrorless-camera-with-18-150mm-lens-5137c009/JXVLXKXGCK/sku/12831670" data-model-name="EOS R7 Mirrorless Camera with 18-150mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/8NfML8ECs8qnx7cmVoCYuA.jpg" alt="BRAND, Canon - EOS R7 Mirrorless Camera with 18-150mm Lens - 5137C009"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>BRAND</div>                                        <div class="featured__title">EOS R7 Mirrorless Camera with 18-150mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ PS5 State of Play September 2026 recap — FFVII Revelations, Until Dawn 2 and every new game announced ]]></title>
                                                                                                <dc:content><![CDATA[ <div class="live-content"><p>PlayStation State of Play for September 2026 is over. We got two showcases for the price of one, with a regular State of Play followed by a State of Play Japan. </p><p>The highlights included an extended look at Final Fantasy VII: Revelation, release dates for Until Dawn 2 and Metro 2039, and new games confirmed like Maneater 2. Plus, State of Play Japan brought with it tons of RPG fun!</p><p>If you missed the showcase, you can find all the announcements and every new trailer below. Or you can even rewatch the entire State of Play on demand. Here are all the games at the State of Play September 2026...</p><h2 id="how-to-re-watch-state-of-play-for-september-2026">How to (re)watch State of Play for September 2026</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/dcTYz_mJpkU" allowfullscreen></iframe></div></div><p>If you missed State of Play September 2026, or wish to relive it, you can watch the entire presentation from start to finish via the embedded YouTube link above. Warning: it's a chunky presentation, so you'll need to set aside almost two hours. After all, it was technically two State of Plays! </p><h3 class="article-body__section" id="section-live-updates"><span>LIVE UPDATES</span></h3></div><div class="live-content"><time datetime="2026-09-03T11:01:21+00:00">September 3, 2026 – 7:01 AM</time><h2 id="welcome">Welcome!</h2><p>Good morning (or afternoon, for those in the U.K.)! The latest State of Play event is just a couple of hours away, and I'm here to bring you all the build-up and coverage of the showcase, there's plenty to discuss, so let's dive into all the news and a few rumors...</p></div><div class="live-content"><time datetime="2026-09-03T11:12:33+00:00">September 3, 2026 – 7:12 AM</time><h2 id="ffvii-revelation-is-confirmed-for-state-of-play">FFVII Revelation is confirmed for State of Play</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="VbV9NhGYbeA4AKitiPGzjc" name="final-fantasy-vii-revelation-screenshot" alt="Screenshot from Final Fantasy VII Revelation" src="https://cdn.mos.cms.futurecdn.net/VbV9NhGYbeA4AKitiPGzjc-1920-80.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: Sqaure Enix)</span></figcaption></figure><p>At present, only one game is confirmed to appear at today's State of Play: Final Fantasy VII Revelation.</p><p>The <a href="https://blog.playstation.com/2026/08/31/state-of-play-state-of-play-japan-return-on-september-3/" target="_blank">PlayStation Blog</a> notes the showcase will conclude "with an extended look at the vast world of Final Fantasy VII Revelation, from our friends at Square Enix."</p><p>The use of the word "extended" suggests it won't be a quick trailer, but likely a longer demo. We've already seen a fair amount from this game. It was announced at Summer Games Fest 2026, before getting another look at Gamescom 2026.</p><p>I'm sure that anybody who's played Final Fantasy VII Remake and Rebirth won't need to be further sold on Revelation, but I won't say no to another look!</p></div><div class="live-content"><time datetime="2026-09-03T11:26:59+00:00">September 3, 2026 – 7:26 AM</time><h2 id="i-suspect-we-39-ll-see-more-from-until-dawn-2">I suspect we'll see more from Until Dawn 2</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="tmER6SQVqEcQekWFWJ4K6b" name="until-dawn-2-screenshot" alt="Until Dawn 2 screenshot" src="https://cdn.mos.cms.futurecdn.net/tmER6SQVqEcQekWFWJ4K6b-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>Turning to predictions, I'm guessing we'll see more from Until Dawn 2 today. This follow-up to the 2014 interactive horror was announced during June's State of Play with a vague 2027 release window, so it would make sense for it to appear here. </p><p>It's confirmed to be a standalone sequel, and comes from new developers Firesprite (taking over from Supermassive Games, who made the first). Like the original, it's a horror adventure game where you play a group of young adults trying to survive a terrifying night. Your decisions will decide who lives and dies.</p><p>The setting switches from a snowy mountain cabin to a tropical island, and the playable characters are a team of ghost hunters who fake their paranormal content, only to face a real nightmare. Maybe we'll get a release date today?</p></div><div class="live-content"><time datetime="2026-09-03T11:46:29+00:00">September 3, 2026 – 7:46 AM</time><h2 id="time-for-our-latest-look-at-laufey">Time for our latest look at Laufey?</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="6uakNK9LNVubhczFUPzQ4R" name="god-of-war-laufey-keyart" alt="God of War Laufey keyart" src="https://cdn.mos.cms.futurecdn.net/6uakNK9LNVubhczFUPzQ4R-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>Staying on theme of games that appeared at the June State of Play that could re appear today, God of Way Laufey seems likely to make an appearance to me. </p><p>The game was revealed and then extensively showcased with a 15-minute gameplay presentation in June, and it's even got an early 2027 release date (February 16), so Sony surely want to start ramping up the marketing soon. </p><p>This latest installment in the God of War franchise doesn't star Kratos, instead the lead is Faye, wife of the angry Spartan. It'll see Faye battle through Everywhen, the afterlife of the gods, on a quest to protect her husband and son, Atreus. </p></div><div class="live-content"><time datetime="2026-09-03T12:03:52+00:00">September 3, 2026 – 8:03 AM</time><h2 id="don-39-t-expect-a-major-quot-one-last-thing-quot-reveal">Don't expect a major "one last thing" reveal</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:920px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="YjQgFQT9J6kopyr5nu4evh" name="Playstation store.jpg" alt="PlayStation Store Holiday Sale" src="https://cdn.mos.cms.futurecdn.net/YjQgFQT9J6kopyr5nu4evh-1920-80.jpg" mos="" align="middle" fullscreen="" width="920" height="518" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>When it comes to State of Play-style events, they're often judged on their ending. The term "one last thing" (taken from Nintendo Directs) has been popularized for when a gaming event ends with a reveal that gets the whole internet hyped up. </p><p>State of Play has had its own share of exciting final announcements in the past, but don't expect one this time. As already noted, Final Fantasy VII Revelation has been confirmed to close the State of Play for September 2026, so there seemingly won't be an unexpected reveal to cap off the livestream. </p><p>Of course, that doesn't mean there won't be surprises before the FFVII portion! </p></div><div class="live-content"><time datetime="2026-09-03T12:15:30+00:00">September 3, 2026 – 8:15 AM</time><h2 id="will-intergalactic-finally-emerge-from-the-shadows">Will Intergalactic finally emerge from the shadows?</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="gGHYy9kM2a4pZMfY2DzSSQ" name="intergalactic-the-heretic-prophet-screenshot" alt="Intergalactic: The Heretic Prophet screenshot" src="https://cdn.mos.cms.futurecdn.net/gGHYy9kM2a4pZMfY2DzSSQ-1920-80.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: Sony / Naughty Dog)</span></figcaption></figure><p>Every State of Play, I wish for a fresh look at Intergalactic: The Heretic Prophet, and every time I'M left disappointed when it's a no show. So, while I'm keeping my expectation in check this time, here's hoping Naughty Dog is <em>finally </em>ready to show us more at today's State of Play. </p><p>The sci-fi game was announced at The Game Awards 2024, with a slick CGI trailer, and hasn't been seen since. There's still so much we don't know about this game, we don't even have a release year, let alone a firm date. It'll be ND's first new game since The Last of Us Part II, and follows a bounty hunter stuck on a remote planet. </p></div><div class="live-content"><time datetime="2026-09-03T12:32:56+00:00">September 3, 2026 – 8:32 AM</time><h2 id="expect-some-ps-news-at-state-of-play">Expect some PS+ news at State of Play</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="YJYKWsoYcKTmcTh2bde6UG" name="playstation-plus-collection-01.jpg" alt="PlayStation Plus Collection feature" src="https://cdn.mos.cms.futurecdn.net/YJYKWsoYcKTmcTh2bde6UG-1920-80.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: PlayStation)</span></figcaption></figure><p>PlayStation typically likes to use State of Play events to highlight the games being added to PS+ in the future, and with the mid-month PlayStation Plus Extra Game Catalogue on the horizon, I wouldn't be surprised if that's showcased later today. </p><p>Maybe those on the Premium tier will also get some news about upcoming Classic games being added to the service. Either way, I expect PS+ will have a presence.</p></div><div class="live-content"><time datetime="2026-09-03T12:54:10+00:00">September 3, 2026 – 8:54 AM</time><h2 id="double-fine-couldn-39-t-keep-this-secret">Double Fine couldn't keep this secret!</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="DjvJiQVxD8e7G76nr5P5Pc" name="keeper-screenshot" alt="Keeper screenshot" src="https://cdn.mos.cms.futurecdn.net/DjvJiQVxD8e7G76nr5P5Pc-1920-80.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: Double Fine)</span></figcaption></figure><p>There's always a leak or two in the lead-up to a State of Play, and it appears this time it's come from Double Fine. The studio's artistic experience, Keeper, looks to be PlayStation-bound, as its trophy list has just surfaced via <a href="https://www.exophase.com/game/keeper-psn/trophies/" target="_blank">Exophase</a>. </p><p>The puzzle-adventure game launched last October on Xbox and PC, when Double Fine was still under the Xbox Games Studio umbrella. It's now gone independent, and appears to be looking to expand Keeper's player base. It'll almost certainly be confirmed during the State of Play. Leaked trophy data is a pretty solid clue. </p><p>As trophy data is already in the PlayStation system, that would hint at an imminent release date; it could even be dropped on the PlayStation Store later today.</p></div><div class="live-content"><time datetime="2026-09-03T12:58:41+00:00">September 3, 2026 – 8:58 AM</time><h2 id="state-of-play-september-2026-is-about-to-begin">State of Play September 2026 is about to begin! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/dcTYz_mJpkU" allowfullscreen></iframe></div></div><p>The countdown is on, and that familiar State of Play pre-show music is playing. If you're watching today's State of Play, now is the time to get your stream ready. Don't worry, you don't need to search for it, I've linked it above, and if you can't watch, I'll have live updates right here so you don't miss a single announcement. </p></div><div class="live-content"><time datetime="2026-09-03T13:02:32+00:00">September 3, 2026 – 9:02 AM</time><h2 id="first-up-is-a-movie">First up...is a movie?</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/KGlapnJFacE" allowfullscreen></iframe></div></div><p>Whelp, it might be a video game event, but we're starting with a trailer for Zach Cregger's upcoming "Resident Evil" movie. </p></div><div class="live-content"><time datetime="2026-09-03T13:04:29+00:00">September 3, 2026 – 9:04 AM</time><h2 id="now-onto-the-games-please">Now onto the games (please)!</h2><p>The new "Resident Evil" movie looks cool, I've already got my tickets booked, but after dipping into the world of cinema, let's hope State of Play gets into the games.</p></div><div class="live-content"><time datetime="2026-09-03T13:06:43+00:00">September 3, 2026 – 9:06 AM</time><h2 id="wolverine-gets-a-last-look-before-launch">Wolverine gets a last look before launch</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/G62QQ42Ewwg" allowfullscreen></iframe></div></div><p>With Marvel's Wolverine landing later this month (on September 15), PlayStation is giving this PS5 exclusive one last State of Play spot before its in our hands soon.</p></div><div class="live-content"><time datetime="2026-09-03T13:08:40+00:00">September 3, 2026 – 9:08 AM</time><h2 id="ghost-of-yotei-39-s-new-dlc-is-next-up">Ghost of Yōtei's new DLC is next up</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/Nlchabfwy_w" allowfullscreen></iframe></div></div><p>It was recently confirmed that 2025's Ghost of Yōte would get a Complete Edition, which comes with new DLC, including the "Most Wanted" mode, and that's being showcased next. It looks like it'll give me plenty of reason to return to the game. </p></div><div class="live-content"><time datetime="2026-09-03T13:11:07+00:00">September 3, 2026 – 9:11 AM</time><h2 id="speaking-of-major-dlcs-it-39-s-monster-hunter-time">Speaking of major DLCs, it's Monster Hunter time!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/SodbU0PEVH4" allowfullscreen></iframe></div></div><p>Another game from last year that's about to get a big DLC is Monster Hunter Wilds, it's called Ascendance, and I'm hoping it'll transform the base game in the same way that Iceborne radically expanded its predecessor Monster Hunter World. It's out in 2027. </p></div><div class="live-content"><time datetime="2026-09-03T13:13:33+00:00">September 3, 2026 – 9:13 AM</time><h2 id="metro-2039-gets-its-first-look-on-playstation">Metro 2039 gets its first look on PlayStation</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/6OGoAWzNV2s" allowfullscreen></iframe></div></div><p>One of my most anticipated games of 2027 takes the State of Play stage: Metro 2039. This is our first time seeing it at a PlayStation event, and I recently went hands-on with the title at Gamescom 2026, and can confirm it's shaping up to be a strong shooter. It's out February 4, 2027. </p></div><div class="live-content"><time datetime="2026-09-03T13:17:12+00:00">September 3, 2026 – 9:17 AM</time><h2 id="maneater-is-getting-a-sequel">Maneater is getting a sequel!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/mg4Phb2Aulg" allowfullscreen></iframe></div></div><p>Shark sim Maneater is getting a sequel! The original launched in 2020, and was a whale of a good time (that pun works, right?). It's called, well, Maneater 2 and is out in 2027. </p></div><div class="live-content"><time datetime="2026-09-03T13:19:00+00:00">September 3, 2026 – 9:19 AM</time><h2 id="another-trailer-for-a-dlc">Another trailer for a DLC!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/1fQCPmH6kOg" allowfullscreen></iframe></div></div><p>DLC expansions seem to be the big focus so far, and next up is an expansion for Crimson Desert. I'll admit, I couldn't really get into this one earlier in the year, but I know it's got a passionate player base. The DLC appears to add naval combat, underwater exploration, and a bunch of new enemies. It's called Crimson Desert: Charting the Unknown and launches on October 15, 2026. That's very soon!</p></div><div class="live-content"><time datetime="2026-09-03T13:20:54+00:00">September 3, 2026 – 9:20 AM</time><h2 id="the-ps1-is-getting-a-lego-makeover">The PS1 is getting a Lego makeover!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/5FB8SIlnd6c" allowfullscreen></iframe></div></div><p>This one leaked earlier in the week, but PlayStation has teamed up with the folks at Lego to create a plastic-brick version of the PlayStation 1 console. It looks like a novel display piece and hides several easter eggs, including dioramas for Ape Escape and Gran Turismo. No word on price or release date, but as it's Lego, it'll probably cost quite a lot!</p></div><div class="live-content"><time datetime="2026-09-03T13:22:44+00:00">September 3, 2026 – 9:22 AM</time><h2 id="final-fantasy-makes-an-early-appearance">Final Fantasy makes an early appearance! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/IZvZYwQdXN8" allowfullscreen></iframe></div></div><p>We know that FFVII Revelation will close the show, but there's room for more Final Fantasy, the game's first title in HD-2D title, Final Fantasy Resonance, is showcased, it's out on October 22, and there's a playable demo dropping later today. Neat!</p></div><div class="live-content"><time datetime="2026-09-03T13:24:42+00:00">September 3, 2026 – 9:24 AM</time><h2 id="gundam-fans-here-39-s-one-for-you">Gundam fans, here's one for you!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/kJ6YB3tG0xM" allowfullscreen></iframe></div></div><p>Gundam: Rogue Orbit was announced earlier in the summer and we're getting a new look at today's State of Play. It's out March 5, 2027, and looks suitably epic. More will be revealed at the State of Play Japan that follows this initial showcase. </p></div><div class="live-content"><time datetime="2026-09-03T13:25:49+00:00">September 3, 2026 – 9:25 AM</time><h2 id="we-39-re-entering-the-indie-zone">We're entering the indie zone!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/bKMaeHoJY2A" allowfullscreen></iframe></div></div><p>Next up, it's a trio of indie games that apparently "caught the teams attention", first it's a medieval-set game called Hunter's Moon that drops on October 26, 2026.  </p></div><div class="live-content"><time datetime="2026-09-03T13:28:46+00:00">September 3, 2026 – 9:28 AM</time><h2 id="uncanyon-is-revealed-and-keeper-is-confirmed">Uncanyon is revealed (and Keeper is confirmed)!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/oSLtfQ-HqLM" allowfullscreen></iframe></div></div><p>Next, we head into the world of Uncanyon, which looks to be a dungeon-crawler that combines puzzles and combat; it's out in 2027. And the final game in this indie trio is Keeper from Double Fine. That one leaked earlier today and is out on PS5 today. Told you it might be a shadow drop!</p></div><div class="live-content"><time datetime="2026-09-03T13:31:01+00:00">September 3, 2026 – 9:31 AM</time><h2 id="rev-noir-gets-a-new-trailer-from-konami">Rev Noir gets a new trailer from Konami!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/7feaOQVG5aE" allowfullscreen></iframe></div></div><p>Rev Noir, technically spelt Rev. NOiR (yes, it's a little silly), was announced at the last State of Play event, and Konami is already prepared with a new trailer for this RPG. </p></div><div class="live-content"><time datetime="2026-09-03T13:34:18+00:00">September 3, 2026 – 9:34 AM</time><h2 id="konami-also-reveals-rhapsody-in-scarlet">Konami also reveals Rhapsody in Scarlet </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/DnMPeZDKiNk" allowfullscreen></iframe></div></div><p>A jazz-set action game called Rhapsody in Scarlet is announced, it comes from Konami, and there will be more details during the State of Play Japan afterwards. </p></div><div class="live-content"><time datetime="2026-09-03T13:36:42+00:00">September 3, 2026 – 9:36 AM</time><h2 id="ps-preview-time-just-as-i-predicted">PS+ preview time (just as I predicted)!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/PpM1KUTh43o" allowfullscreen></iframe></div></div><p>As I guessed, now we're getting an insight into what games are coming to PS+, including Mega Man X and the first Ratchet & Clank in the Classics Catalog. </p><p>Oh, and Saros is getting a big update with some new features; it'll be a free update. And Gran Turismo 7 is also being updated. The theme today is certainly DLCs! </p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/qxFIHS2KhbM" allowfullscreen></iframe></div></div></div><div class="live-content"><time datetime="2026-09-03T13:41:49+00:00">September 3, 2026 – 9:41 AM</time><h2 id="i-39-m-on-a-roll-as-until-dawn-2-makes-an-appearance">I'm on a roll as Until Dawn 2 makes an appearance! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/-wjhso38sAY" allowfullscreen></iframe></div></div><p>I don't want to pat myself on the back too much, but another of my predictions just came true, as the next trailer is for Until Dawn 2, the long awaited sequel sports a new cast, setting, storyline and even developer. It's all change, but the fundamental experience looks similar; it's again an interactive horror where the fate of each character is in your hands. Every decision matters. </p><p>It launches on January 28, 2027, which is sooner than I expected.</p></div><div class="live-content"><time datetime="2026-09-03T13:43:13+00:00">September 3, 2026 – 9:43 AM</time><h2 id="gta-6-is-getting-a-limited-edition-dualsense-controller">GTA 6 is getting a limited edition DualSense controller!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/rcx6I2A0TyM" allowfullscreen></iframe></div></div><p>PlayStation is launching another limited edition DualSense controller, and I reckon this one will be popular because it's for GTA 6. I'm telling myself I don't need <em>another </em>DualSense...</p></div><div class="live-content"><time datetime="2026-09-03T13:45:28+00:00">September 3, 2026 – 9:45 AM</time><h2 id="and-now-into-final-fantasy-vii-revelation">And now into Final Fantasy VII Revelation!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/pfqrUIGUC1o" allowfullscreen></iframe></div></div><p>It was confirmed to be the closer, so with the regular State of Play wrapping up, we've just got a very cinematic trailer for Final Fantasy VII Revelation, and most importantly, it includes a release date: April 8, 2027. </p></div><div class="live-content"><time datetime="2026-09-03T13:49:38+00:00">September 3, 2026 – 9:49 AM</time><h2 id="loads-of-new-ffvii-revelation-gameplay">Loads of new FFVII Revelation gameplay!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/E9BTbjtXJWM" allowfullscreen></iframe></div></div><p>It was called an extended look, and it's living up to its billing. State of Play is ending with a 10 minute Final Fantasy VII Revelation gameplay presentation that is mostly focused on its vast world. You have access to an airship, which should make getting around simple, and if you spot something interesting, you skydive to the ground.</p></div><div class="live-content"><time datetime="2026-09-03T13:57:35+00:00">September 3, 2026 – 9:57 AM</time><h2 id="and-that-39-s-state-of-play-in-the-can-but-there-39-s-more-to-come">And that's State of Play in the can (but there's more to come)</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/dcTYz_mJpkU" allowfullscreen></iframe></div></div><p>FFVII Revelation brings State of Play to an end (as we knew it would). For me, that was a fairly average State of Play overall, but with a few announcements that grabbed my attention. But the show isn't over, as now we head into State of Play Japan, which offers further insight into some of the games revealed today! </p></div><div class="live-content"><time datetime="2026-09-03T14:00:35+00:00">September 3, 2026 – 10:00 AM</time><h2 id="dragonball-xenoverse-3-kicks-off-sop-japan">Dragonball Xenoverse 3 kicks off SoP Japan!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/912l6NX5lrY" allowfullscreen></iframe></div></div><p>The first new trailer in this second State of Play presentation is Dragon Ball Xenoverse 3. </p></div><div class="live-content"><time datetime="2026-09-03T14:03:06+00:00">September 3, 2026 – 10:03 AM</time><h2 id="followed-by-fate-extra-record">Followed by Fate/Extra Record!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/nr5-XGIkbg4" allowfullscreen></iframe></div></div><p>This remake of the first game in the Fate series is coming to PS5 on January 28, 2027 (The same day as Until Dawn 2). Per the trailer it offers "an expanded scenario, completely overhauled character graphics, and a highly strategic deck-building dramatic command battle system, the game modernizes both visuals and mechanics."</p></div><div class="live-content"><time datetime="2026-09-03T14:05:48+00:00">September 3, 2026 – 10:05 AM</time><h2 id="more-from-monster-hunter">More from Monster Hunter!</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="Uy6gzZgNHR8bLp5umbtnv4" name="Monster Hunter Wilds 1" alt="Monster Hunter Wilds" src="https://cdn.mos.cms.futurecdn.net/Uy6gzZgNHR8bLp5umbtnv4-1920-80.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: Capcom)</span></figcaption></figure><p>The next segment is a deeper dive into the Monster Hunter Wilds: Ascendance expansion. It's looking to alter the experience in a bunch of ways with new weapons, armor, abilities, and, of course, most importantly, plenty of new monsters to hunt</p></div><div class="live-content"><time datetime="2026-09-03T14:10:25+00:00">September 3, 2026 – 10:10 AM</time><h2 id="dragon-quest-monsters-launches-this-december">Dragon Quest Monsters launches this December!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/_9nrXc0-vm8" allowfullscreen></iframe></div></div><p>We've had Final Fantasy, now time for the <em>other </em>legendary Japanese RPG series, Dragon Quest. This isn't the latest mainline entry, but part of the spin-off Monsters series. Dragon Quest Monsters: The Withered World launches soon, on December 3, 2026. Watch the new trailer above!</p></div><div class="live-content"><time datetime="2026-09-03T14:12:04+00:00">September 3, 2026 – 10:12 AM</time><h2 id="here-39-s-one-for-the-rogue-lite-fans">Here's one for the rogue-lite fans!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/c9V5ugavBQU" allowfullscreen></iframe></div></div><p>There was a curious lack of rogue-lites in the main State of Play, the genre flooded these days, but State of Play Japan has fixed that with The Eminence in Shadow: Phantom Echoes (quite the title), it's described as a rouge-lite action game launching in 2027! </p></div><div class="live-content"><time datetime="2026-09-03T14:14:57+00:00">September 3, 2026 – 10:14 AM</time><h2 id="more-new-trailers-to-watch">More new trailers to watch!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/MLDGArmX0Tw" allowfullscreen></iframe></div></div><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/zdDJlYAVe-c" allowfullscreen></iframe></div></div><p>The announcements and trailers are coming fast in this State of Play Japan, here's two more, Where the Seeds Fall and one for online soccer game, Rematch, which is getting a collaboration with Blue Lock for its season 5!  </p></div><div class="live-content"><time datetime="2026-09-03T14:19:54+00:00">September 3, 2026 – 10:19 AM</time><h2 id="this-one-looks-intriguing">This one looks intriguing! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/fIFA-lY_f6o" allowfullscreen></iframe></div></div><p>WPCA: World Phasebound Control Authority is showcased next, it looks like to be an action game where you're part of a group protecting our reality from supernatural foes. Pretty cool setup. Though, it's not out until 2028, so we won't be playing it any time soon! </p></div><div class="live-content"><time datetime="2026-09-03T14:21:40+00:00">September 3, 2026 – 10:21 AM</time><h2 id="stupid-never-dies-looks-ridiculous-but-in-a-good-way">Stupid Never Dies looks ridiculous, but in a good way! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/k_33MQFuVEY" allowfullscreen></iframe></div></div><p>GPTRACK50’s Stupid Never Dies gets its third trailer, and this action-RPG looks like it's taking some big swings, and isn't scared to be very silly. I do wonder if the comedic tone might grate over a full playthrough, but it's another one that's caught my eye. </p></div><div class="live-content"><time datetime="2026-09-03T14:23:59+00:00">September 3, 2026 – 10:23 AM</time><h2 id="another-game-you-can-play-today">Another game you can play today!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/nrJuL3XSLTc" allowfullscreen></iframe></div></div><p>SlashZero is getting a demo today. The full game isn't out until Spring 2027, but you can get a taste of it's side-scrollling action thanks to this demo. It's called a "limited-time" demo, so don't wait around if you want to check this one out before launch.</p></div><div class="live-content"><time datetime="2026-09-03T14:26:05+00:00">September 3, 2026 – 10:26 AM</time><h2 id="digimon-takes-the-stage-at-state-of-play-japan">Digimon takes the stage at State of Play Japan!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/8v_Mg3yAnSY" allowfullscreen></iframe></div></div><p>I was always a Digimon kid, while all my friends were into Pokemon, so if you like me have a fondness for digital monsters, check out the trailer for Digimon Story Time Stranger: A Hero's Eternal Legacy, which surely wins the award for the longest game name at today's State of Play.</p></div><div class="live-content"><time datetime="2026-09-03T14:29:45+00:00">September 3, 2026 – 10:29 AM</time><h2 id="conq-looks-rough-in-more-ways-than-one">Conq looks rough, in more ways than one!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/OqViikdsc7Y" allowfullscreen></iframe></div></div><p>CONQ: Crown Of No Quarter just debuted at State of Play Japan, and there are rough seas, and also a very choppy framerate. No release date details so hopefully the development team have lots of time to iron out the wrinkles, as this showing didn't do the game many favors. </p></div><div class="live-content"><time datetime="2026-09-03T14:31:11+00:00">September 3, 2026 – 10:31 AM</time><h2 id="another-rhapsody-in-scarlet-trailer">Another Rhapsody in Scarlet trailer!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/FX6kr_DoXK8" allowfullscreen></iframe></div></div><p>Rhapsody in Scarlet was announced in the State of Play, and anybody interested in the title was that first showing didn't have to wait long for another, State of Play Japan has given us more insight into the game. </p></div><div class="live-content"><time datetime="2026-09-03T14:35:06+00:00">September 3, 2026 – 10:35 AM</time><h2 id="konami-has-gone-big-for-this-state-of-play">Konami has gone big for this State of Play!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/PXmX1PXvDtA" allowfullscreen></iframe></div></div><p>Konami is really bringing the good to today's State of Play, it's also offered a second trailer for Rev Noir. But I'm a little surprised we haven't seen anything from Silent Hill: Townfall as that's due out in just a matter of weeks! </p></div><div class="live-content"><time datetime="2026-09-03T14:36:51+00:00">September 3, 2026 – 10:36 AM</time><h2 id="daba-land-of-water-scar-is-revealed-next">Daba: Land of Water Scar is revealed next! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/YBTN74SHUnk" allowfullscreen></iframe></div></div><p>Looks like there's another Soulslike on the way in the form of Daba: Land of Water Scar, appears to have a mystical theme and the brief snippets of gameplay certainly give off a big Souls vibe. </p></div><div class="live-content"><time datetime="2026-09-03T14:44:59+00:00">September 3, 2026 – 10:44 AM</time><h2 id="ghost-of-yotei-39-s-story-expansion-takes-the-spotlight">Ghost of Yōtei's story expansion takes the spotlight! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/d48IWwIRg98" allowfullscreen></iframe></div></div><p>After a lovely (if slightly lengthy) interview with the Japanese voice actor for Ghost of Yōtei's lead Atsu, now it's time for a trailer for the new story expansion, Echoes of Sekigahara, it'll be packaged with the "Most Wanted" survival mode in the Complete Edition which launches on October 1. </p></div><div class="live-content"><time datetime="2026-09-03T14:46:00+00:00">September 3, 2026 – 10:46 AM</time><h2 id="and-that-39-s-a-wrap-on-state-of-play-japan">And that's a wrap on State of Play Japan!</h2><p>There's just time for a quick sizzle reel covering everything we saw in State of Play Japan, and that's the end of the showcase.</p></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/news/live/ps5-state-of-play-september-2026-live-updates</link>
                                                                            <description>
                            <![CDATA[ State of Play returns for September 2026 with a bumper showcase of new PS5 games, and we're bringing you all the latest updates now. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">F942N5ZiR5UJdXxkW5T6AA</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/PT36mfinkuP9dJs9gKgbBi-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 03 Sep 2026 11:00:16 +0000</pubDate>                                                                                                                                <updated>Thu, 03 Sep 2026 17:17:37 +0000</updated>
                                                                                                                                            <category><![CDATA[Playstation]]></category>
                                                    <category><![CDATA[Gaming]]></category>
                                                                                                <author><![CDATA[ rory.mellon@futurenet.com (Rory Mellon) ]]></author>                    <dc:creator><![CDATA[ Rory Mellon ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/5Rv6LtQXMj5JB4Eu8Lt4Sn-320-70.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/PT36mfinkuP9dJs9gKgbBi-1920-80.jpg">
                                                            <media:credit><![CDATA[Sony Interactive Entertainment]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[God of War Laufey screenshot with State of Play border]]></media:description>                                                            <media:text><![CDATA[God of War Laufey screenshot with State of Play border]]></media:text>
                                <media:title type="plain"><![CDATA[God of War Laufey screenshot with State of Play border]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/PT36mfinkuP9dJs9gKgbBi-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div class="live-content"><p>PlayStation State of Play for September 2026 is over. We got two showcases for the price of one, with a regular State of Play followed by a State of Play Japan. </p><p>The highlights included an extended look at Final Fantasy VII: Revelation, release dates for Until Dawn 2 and Metro 2039, and new games confirmed like Maneater 2. Plus, State of Play Japan brought with it tons of RPG fun!</p><p>If you missed the showcase, you can find all the announcements and every new trailer below. Or you can even rewatch the entire State of Play on demand. Here are all the games at the State of Play September 2026...</p><h2 id="how-to-re-watch-state-of-play-for-september-2026">How to (re)watch State of Play for September 2026</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/dcTYz_mJpkU" allowfullscreen></iframe></div></div><p>If you missed State of Play September 2026, or wish to relive it, you can watch the entire presentation from start to finish via the embedded YouTube link above. Warning: it's a chunky presentation, so you'll need to set aside almost two hours. After all, it was technically two State of Plays! </p><h3 class="article-body__section" id="section-live-updates"><span>LIVE UPDATES</span></h3></div><div class="live-content"><time datetime="2026-09-03T11:01:21+00:00">September 3, 2026 – 7:01 AM</time><h2 id="welcome">Welcome!</h2><p>Good morning (or afternoon, for those in the U.K.)! The latest State of Play event is just a couple of hours away, and I'm here to bring you all the build-up and coverage of the showcase, there's plenty to discuss, so let's dive into all the news and a few rumors...</p></div><div class="live-content"><time datetime="2026-09-03T11:12:33+00:00">September 3, 2026 – 7:12 AM</time><h2 id="ffvii-revelation-is-confirmed-for-state-of-play">FFVII Revelation is confirmed for State of Play</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="VbV9NhGYbeA4AKitiPGzjc" name="final-fantasy-vii-revelation-screenshot" alt="Screenshot from Final Fantasy VII Revelation" src="https://cdn.mos.cms.futurecdn.net/VbV9NhGYbeA4AKitiPGzjc-1920-80.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: Sqaure Enix)</span></figcaption></figure><p>At present, only one game is confirmed to appear at today's State of Play: Final Fantasy VII Revelation.</p><p>The <a href="https://blog.playstation.com/2026/08/31/state-of-play-state-of-play-japan-return-on-september-3/" target="_blank">PlayStation Blog</a> notes the showcase will conclude "with an extended look at the vast world of Final Fantasy VII Revelation, from our friends at Square Enix."</p><p>The use of the word "extended" suggests it won't be a quick trailer, but likely a longer demo. We've already seen a fair amount from this game. It was announced at Summer Games Fest 2026, before getting another look at Gamescom 2026.</p><p>I'm sure that anybody who's played Final Fantasy VII Remake and Rebirth won't need to be further sold on Revelation, but I won't say no to another look!</p></div><div class="live-content"><time datetime="2026-09-03T11:26:59+00:00">September 3, 2026 – 7:26 AM</time><h2 id="i-suspect-we-39-ll-see-more-from-until-dawn-2">I suspect we'll see more from Until Dawn 2</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="tmER6SQVqEcQekWFWJ4K6b" name="until-dawn-2-screenshot" alt="Until Dawn 2 screenshot" src="https://cdn.mos.cms.futurecdn.net/tmER6SQVqEcQekWFWJ4K6b-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>Turning to predictions, I'm guessing we'll see more from Until Dawn 2 today. This follow-up to the 2014 interactive horror was announced during June's State of Play with a vague 2027 release window, so it would make sense for it to appear here. </p><p>It's confirmed to be a standalone sequel, and comes from new developers Firesprite (taking over from Supermassive Games, who made the first). Like the original, it's a horror adventure game where you play a group of young adults trying to survive a terrifying night. Your decisions will decide who lives and dies.</p><p>The setting switches from a snowy mountain cabin to a tropical island, and the playable characters are a team of ghost hunters who fake their paranormal content, only to face a real nightmare. Maybe we'll get a release date today?</p></div><div class="live-content"><time datetime="2026-09-03T11:46:29+00:00">September 3, 2026 – 7:46 AM</time><h2 id="time-for-our-latest-look-at-laufey">Time for our latest look at Laufey?</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="6uakNK9LNVubhczFUPzQ4R" name="god-of-war-laufey-keyart" alt="God of War Laufey keyart" src="https://cdn.mos.cms.futurecdn.net/6uakNK9LNVubhczFUPzQ4R-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>Staying on theme of games that appeared at the June State of Play that could re appear today, God of Way Laufey seems likely to make an appearance to me. </p><p>The game was revealed and then extensively showcased with a 15-minute gameplay presentation in June, and it's even got an early 2027 release date (February 16), so Sony surely want to start ramping up the marketing soon. </p><p>This latest installment in the God of War franchise doesn't star Kratos, instead the lead is Faye, wife of the angry Spartan. It'll see Faye battle through Everywhen, the afterlife of the gods, on a quest to protect her husband and son, Atreus. </p></div><div class="live-content"><time datetime="2026-09-03T12:03:52+00:00">September 3, 2026 – 8:03 AM</time><h2 id="don-39-t-expect-a-major-quot-one-last-thing-quot-reveal">Don't expect a major "one last thing" reveal</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:920px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="YjQgFQT9J6kopyr5nu4evh" name="Playstation store.jpg" alt="PlayStation Store Holiday Sale" src="https://cdn.mos.cms.futurecdn.net/YjQgFQT9J6kopyr5nu4evh-1920-80.jpg" mos="" align="middle" fullscreen="" width="920" height="518" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>When it comes to State of Play-style events, they're often judged on their ending. The term "one last thing" (taken from Nintendo Directs) has been popularized for when a gaming event ends with a reveal that gets the whole internet hyped up. </p><p>State of Play has had its own share of exciting final announcements in the past, but don't expect one this time. As already noted, Final Fantasy VII Revelation has been confirmed to close the State of Play for September 2026, so there seemingly won't be an unexpected reveal to cap off the livestream. </p><p>Of course, that doesn't mean there won't be surprises before the FFVII portion! </p></div><div class="live-content"><time datetime="2026-09-03T12:15:30+00:00">September 3, 2026 – 8:15 AM</time><h2 id="will-intergalactic-finally-emerge-from-the-shadows">Will Intergalactic finally emerge from the shadows?</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="gGHYy9kM2a4pZMfY2DzSSQ" name="intergalactic-the-heretic-prophet-screenshot" alt="Intergalactic: The Heretic Prophet screenshot" src="https://cdn.mos.cms.futurecdn.net/gGHYy9kM2a4pZMfY2DzSSQ-1920-80.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: Sony / Naughty Dog)</span></figcaption></figure><p>Every State of Play, I wish for a fresh look at Intergalactic: The Heretic Prophet, and every time I'M left disappointed when it's a no show. So, while I'm keeping my expectation in check this time, here's hoping Naughty Dog is <em>finally </em>ready to show us more at today's State of Play. </p><p>The sci-fi game was announced at The Game Awards 2024, with a slick CGI trailer, and hasn't been seen since. There's still so much we don't know about this game, we don't even have a release year, let alone a firm date. It'll be ND's first new game since The Last of Us Part II, and follows a bounty hunter stuck on a remote planet. </p></div><div class="live-content"><time datetime="2026-09-03T12:32:56+00:00">September 3, 2026 – 8:32 AM</time><h2 id="expect-some-ps-news-at-state-of-play">Expect some PS+ news at State of Play</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="YJYKWsoYcKTmcTh2bde6UG" name="playstation-plus-collection-01.jpg" alt="PlayStation Plus Collection feature" src="https://cdn.mos.cms.futurecdn.net/YJYKWsoYcKTmcTh2bde6UG-1920-80.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: PlayStation)</span></figcaption></figure><p>PlayStation typically likes to use State of Play events to highlight the games being added to PS+ in the future, and with the mid-month PlayStation Plus Extra Game Catalogue on the horizon, I wouldn't be surprised if that's showcased later today. </p><p>Maybe those on the Premium tier will also get some news about upcoming Classic games being added to the service. Either way, I expect PS+ will have a presence.</p></div><div class="live-content"><time datetime="2026-09-03T12:54:10+00:00">September 3, 2026 – 8:54 AM</time><h2 id="double-fine-couldn-39-t-keep-this-secret">Double Fine couldn't keep this secret!</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="DjvJiQVxD8e7G76nr5P5Pc" name="keeper-screenshot" alt="Keeper screenshot" src="https://cdn.mos.cms.futurecdn.net/DjvJiQVxD8e7G76nr5P5Pc-1920-80.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: Double Fine)</span></figcaption></figure><p>There's always a leak or two in the lead-up to a State of Play, and it appears this time it's come from Double Fine. The studio's artistic experience, Keeper, looks to be PlayStation-bound, as its trophy list has just surfaced via <a href="https://www.exophase.com/game/keeper-psn/trophies/" target="_blank">Exophase</a>. </p><p>The puzzle-adventure game launched last October on Xbox and PC, when Double Fine was still under the Xbox Games Studio umbrella. It's now gone independent, and appears to be looking to expand Keeper's player base. It'll almost certainly be confirmed during the State of Play. Leaked trophy data is a pretty solid clue. </p><p>As trophy data is already in the PlayStation system, that would hint at an imminent release date; it could even be dropped on the PlayStation Store later today.</p></div><div class="live-content"><time datetime="2026-09-03T12:58:41+00:00">September 3, 2026 – 8:58 AM</time><h2 id="state-of-play-september-2026-is-about-to-begin">State of Play September 2026 is about to begin! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/dcTYz_mJpkU" allowfullscreen></iframe></div></div><p>The countdown is on, and that familiar State of Play pre-show music is playing. If you're watching today's State of Play, now is the time to get your stream ready. Don't worry, you don't need to search for it, I've linked it above, and if you can't watch, I'll have live updates right here so you don't miss a single announcement. </p></div><div class="live-content"><time datetime="2026-09-03T13:02:32+00:00">September 3, 2026 – 9:02 AM</time><h2 id="first-up-is-a-movie">First up...is a movie?</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/KGlapnJFacE" allowfullscreen></iframe></div></div><p>Whelp, it might be a video game event, but we're starting with a trailer for Zach Cregger's upcoming "Resident Evil" movie. </p></div><div class="live-content"><time datetime="2026-09-03T13:04:29+00:00">September 3, 2026 – 9:04 AM</time><h2 id="now-onto-the-games-please">Now onto the games (please)!</h2><p>The new "Resident Evil" movie looks cool, I've already got my tickets booked, but after dipping into the world of cinema, let's hope State of Play gets into the games.</p></div><div class="live-content"><time datetime="2026-09-03T13:06:43+00:00">September 3, 2026 – 9:06 AM</time><h2 id="wolverine-gets-a-last-look-before-launch">Wolverine gets a last look before launch</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/G62QQ42Ewwg" allowfullscreen></iframe></div></div><p>With Marvel's Wolverine landing later this month (on September 15), PlayStation is giving this PS5 exclusive one last State of Play spot before its in our hands soon.</p></div><div class="live-content"><time datetime="2026-09-03T13:08:40+00:00">September 3, 2026 – 9:08 AM</time><h2 id="ghost-of-yotei-39-s-new-dlc-is-next-up">Ghost of Yōtei's new DLC is next up</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/Nlchabfwy_w" allowfullscreen></iframe></div></div><p>It was recently confirmed that 2025's Ghost of Yōte would get a Complete Edition, which comes with new DLC, including the "Most Wanted" mode, and that's being showcased next. It looks like it'll give me plenty of reason to return to the game. </p></div><div class="live-content"><time datetime="2026-09-03T13:11:07+00:00">September 3, 2026 – 9:11 AM</time><h2 id="speaking-of-major-dlcs-it-39-s-monster-hunter-time">Speaking of major DLCs, it's Monster Hunter time!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/SodbU0PEVH4" allowfullscreen></iframe></div></div><p>Another game from last year that's about to get a big DLC is Monster Hunter Wilds, it's called Ascendance, and I'm hoping it'll transform the base game in the same way that Iceborne radically expanded its predecessor Monster Hunter World. It's out in 2027. </p></div><div class="live-content"><time datetime="2026-09-03T13:13:33+00:00">September 3, 2026 – 9:13 AM</time><h2 id="metro-2039-gets-its-first-look-on-playstation">Metro 2039 gets its first look on PlayStation</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/6OGoAWzNV2s" allowfullscreen></iframe></div></div><p>One of my most anticipated games of 2027 takes the State of Play stage: Metro 2039. This is our first time seeing it at a PlayStation event, and I recently went hands-on with the title at Gamescom 2026, and can confirm it's shaping up to be a strong shooter. It's out February 4, 2027. </p></div><div class="live-content"><time datetime="2026-09-03T13:17:12+00:00">September 3, 2026 – 9:17 AM</time><h2 id="maneater-is-getting-a-sequel">Maneater is getting a sequel!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/mg4Phb2Aulg" allowfullscreen></iframe></div></div><p>Shark sim Maneater is getting a sequel! The original launched in 2020, and was a whale of a good time (that pun works, right?). It's called, well, Maneater 2 and is out in 2027. </p></div><div class="live-content"><time datetime="2026-09-03T13:19:00+00:00">September 3, 2026 – 9:19 AM</time><h2 id="another-trailer-for-a-dlc">Another trailer for a DLC!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/1fQCPmH6kOg" allowfullscreen></iframe></div></div><p>DLC expansions seem to be the big focus so far, and next up is an expansion for Crimson Desert. I'll admit, I couldn't really get into this one earlier in the year, but I know it's got a passionate player base. The DLC appears to add naval combat, underwater exploration, and a bunch of new enemies. It's called Crimson Desert: Charting the Unknown and launches on October 15, 2026. That's very soon!</p></div><div class="live-content"><time datetime="2026-09-03T13:20:54+00:00">September 3, 2026 – 9:20 AM</time><h2 id="the-ps1-is-getting-a-lego-makeover">The PS1 is getting a Lego makeover!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/5FB8SIlnd6c" allowfullscreen></iframe></div></div><p>This one leaked earlier in the week, but PlayStation has teamed up with the folks at Lego to create a plastic-brick version of the PlayStation 1 console. It looks like a novel display piece and hides several easter eggs, including dioramas for Ape Escape and Gran Turismo. No word on price or release date, but as it's Lego, it'll probably cost quite a lot!</p></div><div class="live-content"><time datetime="2026-09-03T13:22:44+00:00">September 3, 2026 – 9:22 AM</time><h2 id="final-fantasy-makes-an-early-appearance">Final Fantasy makes an early appearance! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/IZvZYwQdXN8" allowfullscreen></iframe></div></div><p>We know that FFVII Revelation will close the show, but there's room for more Final Fantasy, the game's first title in HD-2D title, Final Fantasy Resonance, is showcased, it's out on October 22, and there's a playable demo dropping later today. Neat!</p></div><div class="live-content"><time datetime="2026-09-03T13:24:42+00:00">September 3, 2026 – 9:24 AM</time><h2 id="gundam-fans-here-39-s-one-for-you">Gundam fans, here's one for you!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/kJ6YB3tG0xM" allowfullscreen></iframe></div></div><p>Gundam: Rogue Orbit was announced earlier in the summer and we're getting a new look at today's State of Play. It's out March 5, 2027, and looks suitably epic. More will be revealed at the State of Play Japan that follows this initial showcase. </p></div><div class="live-content"><time datetime="2026-09-03T13:25:49+00:00">September 3, 2026 – 9:25 AM</time><h2 id="we-39-re-entering-the-indie-zone">We're entering the indie zone!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/bKMaeHoJY2A" allowfullscreen></iframe></div></div><p>Next up, it's a trio of indie games that apparently "caught the teams attention", first it's a medieval-set game called Hunter's Moon that drops on October 26, 2026.  </p></div><div class="live-content"><time datetime="2026-09-03T13:28:46+00:00">September 3, 2026 – 9:28 AM</time><h2 id="uncanyon-is-revealed-and-keeper-is-confirmed">Uncanyon is revealed (and Keeper is confirmed)!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/oSLtfQ-HqLM" allowfullscreen></iframe></div></div><p>Next, we head into the world of Uncanyon, which looks to be a dungeon-crawler that combines puzzles and combat; it's out in 2027. And the final game in this indie trio is Keeper from Double Fine. That one leaked earlier today and is out on PS5 today. Told you it might be a shadow drop!</p></div><div class="live-content"><time datetime="2026-09-03T13:31:01+00:00">September 3, 2026 – 9:31 AM</time><h2 id="rev-noir-gets-a-new-trailer-from-konami">Rev Noir gets a new trailer from Konami!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/7feaOQVG5aE" allowfullscreen></iframe></div></div><p>Rev Noir, technically spelt Rev. NOiR (yes, it's a little silly), was announced at the last State of Play event, and Konami is already prepared with a new trailer for this RPG. </p></div><div class="live-content"><time datetime="2026-09-03T13:34:18+00:00">September 3, 2026 – 9:34 AM</time><h2 id="konami-also-reveals-rhapsody-in-scarlet">Konami also reveals Rhapsody in Scarlet </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/DnMPeZDKiNk" allowfullscreen></iframe></div></div><p>A jazz-set action game called Rhapsody in Scarlet is announced, it comes from Konami, and there will be more details during the State of Play Japan afterwards. </p></div><div class="live-content"><time datetime="2026-09-03T13:36:42+00:00">September 3, 2026 – 9:36 AM</time><h2 id="ps-preview-time-just-as-i-predicted">PS+ preview time (just as I predicted)!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/PpM1KUTh43o" allowfullscreen></iframe></div></div><p>As I guessed, now we're getting an insight into what games are coming to PS+, including Mega Man X and the first Ratchet & Clank in the Classics Catalog. </p><p>Oh, and Saros is getting a big update with some new features; it'll be a free update. And Gran Turismo 7 is also being updated. The theme today is certainly DLCs! </p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/qxFIHS2KhbM" allowfullscreen></iframe></div></div></div><div class="live-content"><time datetime="2026-09-03T13:41:49+00:00">September 3, 2026 – 9:41 AM</time><h2 id="i-39-m-on-a-roll-as-until-dawn-2-makes-an-appearance">I'm on a roll as Until Dawn 2 makes an appearance! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/-wjhso38sAY" allowfullscreen></iframe></div></div><p>I don't want to pat myself on the back too much, but another of my predictions just came true, as the next trailer is for Until Dawn 2, the long awaited sequel sports a new cast, setting, storyline and even developer. It's all change, but the fundamental experience looks similar; it's again an interactive horror where the fate of each character is in your hands. Every decision matters. </p><p>It launches on January 28, 2027, which is sooner than I expected.</p></div><div class="live-content"><time datetime="2026-09-03T13:43:13+00:00">September 3, 2026 – 9:43 AM</time><h2 id="gta-6-is-getting-a-limited-edition-dualsense-controller">GTA 6 is getting a limited edition DualSense controller!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/rcx6I2A0TyM" allowfullscreen></iframe></div></div><p>PlayStation is launching another limited edition DualSense controller, and I reckon this one will be popular because it's for GTA 6. I'm telling myself I don't need <em>another </em>DualSense...</p></div><div class="live-content"><time datetime="2026-09-03T13:45:28+00:00">September 3, 2026 – 9:45 AM</time><h2 id="and-now-into-final-fantasy-vii-revelation">And now into Final Fantasy VII Revelation!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/pfqrUIGUC1o" allowfullscreen></iframe></div></div><p>It was confirmed to be the closer, so with the regular State of Play wrapping up, we've just got a very cinematic trailer for Final Fantasy VII Revelation, and most importantly, it includes a release date: April 8, 2027. </p></div><div class="live-content"><time datetime="2026-09-03T13:49:38+00:00">September 3, 2026 – 9:49 AM</time><h2 id="loads-of-new-ffvii-revelation-gameplay">Loads of new FFVII Revelation gameplay!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/E9BTbjtXJWM" allowfullscreen></iframe></div></div><p>It was called an extended look, and it's living up to its billing. State of Play is ending with a 10 minute Final Fantasy VII Revelation gameplay presentation that is mostly focused on its vast world. You have access to an airship, which should make getting around simple, and if you spot something interesting, you skydive to the ground.</p></div><div class="live-content"><time datetime="2026-09-03T13:57:35+00:00">September 3, 2026 – 9:57 AM</time><h2 id="and-that-39-s-state-of-play-in-the-can-but-there-39-s-more-to-come">And that's State of Play in the can (but there's more to come)</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/dcTYz_mJpkU" allowfullscreen></iframe></div></div><p>FFVII Revelation brings State of Play to an end (as we knew it would). For me, that was a fairly average State of Play overall, but with a few announcements that grabbed my attention. But the show isn't over, as now we head into State of Play Japan, which offers further insight into some of the games revealed today! </p></div><div class="live-content"><time datetime="2026-09-03T14:00:35+00:00">September 3, 2026 – 10:00 AM</time><h2 id="dragonball-xenoverse-3-kicks-off-sop-japan">Dragonball Xenoverse 3 kicks off SoP Japan!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/912l6NX5lrY" allowfullscreen></iframe></div></div><p>The first new trailer in this second State of Play presentation is Dragon Ball Xenoverse 3. </p></div><div class="live-content"><time datetime="2026-09-03T14:03:06+00:00">September 3, 2026 – 10:03 AM</time><h2 id="followed-by-fate-extra-record">Followed by Fate/Extra Record!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/nr5-XGIkbg4" allowfullscreen></iframe></div></div><p>This remake of the first game in the Fate series is coming to PS5 on January 28, 2027 (The same day as Until Dawn 2). Per the trailer it offers "an expanded scenario, completely overhauled character graphics, and a highly strategic deck-building dramatic command battle system, the game modernizes both visuals and mechanics."</p></div><div class="live-content"><time datetime="2026-09-03T14:05:48+00:00">September 3, 2026 – 10:05 AM</time><h2 id="more-from-monster-hunter">More from Monster Hunter!</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="Uy6gzZgNHR8bLp5umbtnv4" name="Monster Hunter Wilds 1" alt="Monster Hunter Wilds" src="https://cdn.mos.cms.futurecdn.net/Uy6gzZgNHR8bLp5umbtnv4-1920-80.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: Capcom)</span></figcaption></figure><p>The next segment is a deeper dive into the Monster Hunter Wilds: Ascendance expansion. It's looking to alter the experience in a bunch of ways with new weapons, armor, abilities, and, of course, most importantly, plenty of new monsters to hunt</p></div><div class="live-content"><time datetime="2026-09-03T14:10:25+00:00">September 3, 2026 – 10:10 AM</time><h2 id="dragon-quest-monsters-launches-this-december">Dragon Quest Monsters launches this December!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/_9nrXc0-vm8" allowfullscreen></iframe></div></div><p>We've had Final Fantasy, now time for the <em>other </em>legendary Japanese RPG series, Dragon Quest. This isn't the latest mainline entry, but part of the spin-off Monsters series. Dragon Quest Monsters: The Withered World launches soon, on December 3, 2026. Watch the new trailer above!</p></div><div class="live-content"><time datetime="2026-09-03T14:12:04+00:00">September 3, 2026 – 10:12 AM</time><h2 id="here-39-s-one-for-the-rogue-lite-fans">Here's one for the rogue-lite fans!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/c9V5ugavBQU" allowfullscreen></iframe></div></div><p>There was a curious lack of rogue-lites in the main State of Play, the genre flooded these days, but State of Play Japan has fixed that with The Eminence in Shadow: Phantom Echoes (quite the title), it's described as a rouge-lite action game launching in 2027! </p></div><div class="live-content"><time datetime="2026-09-03T14:14:57+00:00">September 3, 2026 – 10:14 AM</time><h2 id="more-new-trailers-to-watch">More new trailers to watch!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/MLDGArmX0Tw" allowfullscreen></iframe></div></div><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/zdDJlYAVe-c" allowfullscreen></iframe></div></div><p>The announcements and trailers are coming fast in this State of Play Japan, here's two more, Where the Seeds Fall and one for online soccer game, Rematch, which is getting a collaboration with Blue Lock for its season 5!  </p></div><div class="live-content"><time datetime="2026-09-03T14:19:54+00:00">September 3, 2026 – 10:19 AM</time><h2 id="this-one-looks-intriguing">This one looks intriguing! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/fIFA-lY_f6o" allowfullscreen></iframe></div></div><p>WPCA: World Phasebound Control Authority is showcased next, it looks like to be an action game where you're part of a group protecting our reality from supernatural foes. Pretty cool setup. Though, it's not out until 2028, so we won't be playing it any time soon! </p></div><div class="live-content"><time datetime="2026-09-03T14:21:40+00:00">September 3, 2026 – 10:21 AM</time><h2 id="stupid-never-dies-looks-ridiculous-but-in-a-good-way">Stupid Never Dies looks ridiculous, but in a good way! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/k_33MQFuVEY" allowfullscreen></iframe></div></div><p>GPTRACK50’s Stupid Never Dies gets its third trailer, and this action-RPG looks like it's taking some big swings, and isn't scared to be very silly. I do wonder if the comedic tone might grate over a full playthrough, but it's another one that's caught my eye. </p></div><div class="live-content"><time datetime="2026-09-03T14:23:59+00:00">September 3, 2026 – 10:23 AM</time><h2 id="another-game-you-can-play-today">Another game you can play today!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/nrJuL3XSLTc" allowfullscreen></iframe></div></div><p>SlashZero is getting a demo today. The full game isn't out until Spring 2027, but you can get a taste of it's side-scrollling action thanks to this demo. It's called a "limited-time" demo, so don't wait around if you want to check this one out before launch.</p></div><div class="live-content"><time datetime="2026-09-03T14:26:05+00:00">September 3, 2026 – 10:26 AM</time><h2 id="digimon-takes-the-stage-at-state-of-play-japan">Digimon takes the stage at State of Play Japan!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/8v_Mg3yAnSY" allowfullscreen></iframe></div></div><p>I was always a Digimon kid, while all my friends were into Pokemon, so if you like me have a fondness for digital monsters, check out the trailer for Digimon Story Time Stranger: A Hero's Eternal Legacy, which surely wins the award for the longest game name at today's State of Play.</p></div><div class="live-content"><time datetime="2026-09-03T14:29:45+00:00">September 3, 2026 – 10:29 AM</time><h2 id="conq-looks-rough-in-more-ways-than-one">Conq looks rough, in more ways than one!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/OqViikdsc7Y" allowfullscreen></iframe></div></div><p>CONQ: Crown Of No Quarter just debuted at State of Play Japan, and there are rough seas, and also a very choppy framerate. No release date details so hopefully the development team have lots of time to iron out the wrinkles, as this showing didn't do the game many favors. </p></div><div class="live-content"><time datetime="2026-09-03T14:31:11+00:00">September 3, 2026 – 10:31 AM</time><h2 id="another-rhapsody-in-scarlet-trailer">Another Rhapsody in Scarlet trailer!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/FX6kr_DoXK8" allowfullscreen></iframe></div></div><p>Rhapsody in Scarlet was announced in the State of Play, and anybody interested in the title was that first showing didn't have to wait long for another, State of Play Japan has given us more insight into the game. </p></div><div class="live-content"><time datetime="2026-09-03T14:35:06+00:00">September 3, 2026 – 10:35 AM</time><h2 id="konami-has-gone-big-for-this-state-of-play">Konami has gone big for this State of Play!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/PXmX1PXvDtA" allowfullscreen></iframe></div></div><p>Konami is really bringing the good to today's State of Play, it's also offered a second trailer for Rev Noir. But I'm a little surprised we haven't seen anything from Silent Hill: Townfall as that's due out in just a matter of weeks! </p></div><div class="live-content"><time datetime="2026-09-03T14:36:51+00:00">September 3, 2026 – 10:36 AM</time><h2 id="daba-land-of-water-scar-is-revealed-next">Daba: Land of Water Scar is revealed next! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/YBTN74SHUnk" allowfullscreen></iframe></div></div><p>Looks like there's another Soulslike on the way in the form of Daba: Land of Water Scar, appears to have a mystical theme and the brief snippets of gameplay certainly give off a big Souls vibe. </p></div><div class="live-content"><time datetime="2026-09-03T14:44:59+00:00">September 3, 2026 – 10:44 AM</time><h2 id="ghost-of-yotei-39-s-story-expansion-takes-the-spotlight">Ghost of Yōtei's story expansion takes the spotlight! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/d48IWwIRg98" allowfullscreen></iframe></div></div><p>After a lovely (if slightly lengthy) interview with the Japanese voice actor for Ghost of Yōtei's lead Atsu, now it's time for a trailer for the new story expansion, Echoes of Sekigahara, it'll be packaged with the "Most Wanted" survival mode in the Complete Edition which launches on October 1. </p></div><div class="live-content"><time datetime="2026-09-03T14:46:00+00:00">September 3, 2026 – 10:46 AM</time><h2 id="and-that-39-s-a-wrap-on-state-of-play-japan">And that's a wrap on State of Play Japan!</h2><p>There's just time for a quick sizzle reel covering everything we saw in State of Play Japan, and that's the end of the showcase.</p></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I listened to 10 of Sony’s new ULT party speakers together — and they offer something for everyone ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Today, on 1 September 2026, Sony introduced three new party speakers: Sony ULT Tower 5, Sony ULT Tower 7, and Sony ULT Tower Max. They build on the success of the previous generation of ULT Tower speakers, like the Tower 10, while introducing new, fun features that bring parties, DJ sets, and karaoke sessions to life.</p><p>The second generation features a new design philosophy and more sophisticated reactive lighting, a new ULT button on the front of each speaker (for changing EQ presets), 360° sound, and better, more powerful drivers. Battery life has been improved across all three speakers, too, as has durability and portability.</p><p>I love testing party speakers because I love headbanging to my favorite tunes. Could the ULT Towers make me love them more than I adore the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/marshall-bromley-750-review">Marshall Bromley 750</a> and the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/marshall-bromley-450-review">Marshall Bromley 450</a>? While I haven't had time to do full reviews of the new speakers, I've spent just under an hour listening to them at a pre-launch event. In fact, I listened to <em>10 </em>of them together. My first impressions? Quite positive, so allow me to share with you what I think of the Tower 5, Tower 7 and Tower Max. </p><h2 id="meet-the-ult-tower-max-tower-7-and-tower-5">Meet the ULT Tower Max, Tower 7 and Tower 5</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3000px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="ng8LAPWC4c8Sn4sniJbkAC" name="Sony_ULT_speakers-5" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/ng8LAPWC4c8Sn4sniJbkAC-1920-80.jpg" mos="" align="middle" fullscreen="" width="3000" height="1687" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>The Sony ULT Tower Max, Tower 7 and Tower 5 are bass-first party speakers that, according to Sony, deliver the most powerful bass yet in the brand's ULT Power Sound series. All three speakers are designed for music lovers, and they combine deep bass, high sound pressure, and party features for an unforgettable experience.</p><p>Regardless of the speaker you buy, you'll be getting XLR, RCA, USB-C and USB-A connectivity for hooking up mics for karaoke, DJ mixers, as well as instruments — making the ULT Towers perfect for nearly every occasion. Each speaker is also fitted with four handles and wheels for easy transport... though you may still need another pair of hands to help with the gargantuan Max.</p><p>The Tower Max, Tower 7 and Tower 5 are IPX4-rated so they're splash-proof. I'm surprised that they aren't more water-resistant, like the IP54-rated Marshall Bromley 750. That will limit the speakers' use outdoors — but really, you shouldn't be using electronic devices in the pouring rain anyway.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2566px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="gQ6MfgWD9B6XkqPcmXvquC" name="Sony_ULT_speakers-2" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/v2/w:1920,t:0,l:0,cw:2566,ch:1443,q:80/gQ6MfgWD9B6XkqPcmXvquC.png" mos="" align="middle" fullscreen="" width="2566" height="1446" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>Sony has worked with four acclaimed (and Grammy-nominated) hip-hop sound creators — Dot Da Genius, Daru Jones, DJ Dready, and Dennis "ROC.am" Jones — to fine-tune the ULT Towers' driver setup and sound profile. As a result, the speakers deliver distortion-free sound across the volume scale.</p><p>The Tower Max packs three 280mm woofers, two mid-range drivers, and four tweeters, so you're getting a lot of power. The Tower 7 is fitted with a single 280mm woofer, two mid-range drivers, and four tweeters, too. Lastly, the Tower 5 features a 230mm woofer, two mid-range drivers, and two tweeters. That's <em>a lot </em>for the lineup's smallest speaker!</p><p>You also get a customizable 10-band equalizer with all three speakers, and the new ULT button on the front grille now lets you swap between EQ presets. These include ULT1 (deep bass, low frequency sound), ULT2 (rumbling bass), Flat (true-to-source), and Live (spacious).</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:7070px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vfgsx9yAq73j7SRCCi4VBD" name="Sony_ULT_speakers-3" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/vfgsx9yAq73j7SRCCi4VBD-1920-80.jpg" mos="" align="middle" fullscreen="" width="7070" height="3977" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>There's really no dearth of features, which is excellent. And the ULT Towers sound like great value for money, too, as they aren't that expensive. I was personally quite surprised when the Sony representatives shared the retail prices with me. The Tower Max is the most expensive of the lot, at <a href="https://www.amazon.com/dp/B0HF5Z52X6" target="_blank" rel="nofollow">$1,499</a> / ~ £1,300; the Tower 7 is the mid-range option, at <a href="https://www.amazon.com/dp/B0HF6FGCZ3" target="_blank" rel="nofollow">$649</a> / ~ £600; and the Tower 5 is the cheapest option, at <a href="https://www.amazon.com/dp/B0HF67ZGC2" target="_blank" rel="nofollow">$449</a> / ~ £400.</p><h2 id="so-what-do-they-sound-like">So, what do they sound like?</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:1996px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="SrCNanAfRwUm7NzVb2UonB" name="Sony_ULT_speakers-6" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/SrCNanAfRwUm7NzVb2UonB-1920-80.jpg" mos="" align="middle" fullscreen="" width="1996" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>Driver sizes and specifications are all for naught if the speakers don't sound great — and in my short time with the Sony ULT Towers, I thought they sounded fantastic. At the pre-launch event, the Sony representatives played songs from different genres to show what the Tower Max, Tower 7 and Tower 5 were capable of.</p><p>Regardless of the song they played, I found myself grinning from ear to ear. All three speakers sounded beautiful. The bass sounded rich and meaty with plenty of low-end impact, and I thoroughly enjoyed the ULT2 EQ preset which made me feel the bass in my chest, temples and deep within my soul.</p><p>The Sony representatives played a song by Dua Lipa, too, and though I'm personally not a huge fan of hers, I couldn't help but tap my feet and bob my head along to the tune. I believe that's a true sign of a competent party speaker. If it can make you dance to artists you don't care for, it's done its job.</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:3000px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="qJxYLxmSggyBmSkTfBKafC" name="Sony_ULT_speakers-4" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/qJxYLxmSggyBmSkTfBKafC-1920-80.jpg" mos="" align="middle" fullscreen="" width="3000" height="1687" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>I also listened to two Tower 7s together in stereo mode with left and right channels. When 'Money' by Pink Floyd started playing and the sound of cash registers and coins traveled between the speakers, I was left extremely impressed. The high-pitched coins didn't sound like they were piercing through the soundstage. The treble sounded shiny and vibrant but not overzealous or fatiguing.</p><p>Finally, I listened to an electronic track on 10 ULT Towers all at once. It's not everyday you get to do something like that — how many of us have such extravagant setups in our basements, eh? The wall of speakers sounded phenomenal. The room-shaking bass made me feel like I was at a gig or a music festival.</p><h2 id="looking-ahead">Looking ahead...</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:7070px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vfgsx9yAq73j7SRCCi4VBD" name="Sony_ULT_speakers-3" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/vfgsx9yAq73j7SRCCi4VBD-1920-80.jpg" mos="" align="middle" fullscreen="" width="7070" height="3977" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>So, Sony's latest generation of party speakers sounds excellent — on paper and in real-world use. The Tower Max, Tower 7 and Tower 5 offer something for everyone, and given how affordable they are (especially the Tower 5), they offer bang for your buck. That's based on my first impressions, of course, and it's subject to change when I do my in-depth testing.</p><p>Speaking of, I'm really looking forward to putting the Tower Max, Tower 7 and Tower 5 through their paces. Do these party speakers sound like they'd be up your street? Let me know in the comments below!</p><p><em>The Sony ULT Tower Max, Tower 7 and Tower 5 will be available to buy from Amazon U.S. and U.K., Best Buy, or </em><a href="http://sony.com/" target="_blank" rel="nofollow"><em>directly from Sony</em></a><em>.</em></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/audio/bluetooth-speakers/marshall-bromley-450-versus-marshall-bromley-750-face-off">I tested the Marshall Bromley 450 and the Marshall Bromley 750 party speakers — and the winner surprised me</a></li><li><a href="https://www.tomsguide.com/audio/bluetooth-speakers/forget-the-bose-s1-pro-the-brand-new-marshall-bromley-450-just-redefined-party-speakers">There's no better party speaker on the planet than the brand-new Marshall Bromley 450</a></li><li><a href="https://www.tomsguide.com/audio/bluetooth-speakers/as-a-guitarist-and-an-audiophile-this-is-the-all-in-one-speaker-ive-been-waiting-for">As a guitarist and an audiophile, this is the all-in-one speaker I’ve been waiting for</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/bluetooth-speakers/sony-ult-tower-party-speakers-hands-on</link>
                                                                            <description>
                            <![CDATA[ Sony has unveiled its next generation of party speakers: the ULT Tower Max, ULT Tower 7 and ULT Tower 5 are here, and they boast some impressive features. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">HkohLSDZE7NEPsGe8jpWvK</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/QnKknrtrJM5M99V3wB73w8-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 01 Sep 2026 16:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Bluetooth Speakers]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Speakers]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3-320-70.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres.&lt;/p&gt;&lt;p&gt;A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&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/QnKknrtrJM5M99V3wB73w8-1920-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[An arrangement of Sony ULT Tower 5, ULT Tower 7 and ULT Tower Max]]></media:description>                                                            <media:text><![CDATA[An arrangement of Sony ULT Tower 5, ULT Tower 7 and ULT Tower Max]]></media:text>
                                <media:title type="plain"><![CDATA[An arrangement of Sony ULT Tower 5, ULT Tower 7 and ULT Tower Max]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/QnKknrtrJM5M99V3wB73w8-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Today, on 1 September 2026, Sony introduced three new party speakers: Sony ULT Tower 5, Sony ULT Tower 7, and Sony ULT Tower Max. They build on the success of the previous generation of ULT Tower speakers, like the Tower 10, while introducing new, fun features that bring parties, DJ sets, and karaoke sessions to life.</p><p>The second generation features a new design philosophy and more sophisticated reactive lighting, a new ULT button on the front of each speaker (for changing EQ presets), 360° sound, and better, more powerful drivers. Battery life has been improved across all three speakers, too, as has durability and portability.</p><p>I love testing party speakers because I love headbanging to my favorite tunes. Could the ULT Towers make me love them more than I adore the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/marshall-bromley-750-review">Marshall Bromley 750</a> and the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/marshall-bromley-450-review">Marshall Bromley 450</a>? While I haven't had time to do full reviews of the new speakers, I've spent just under an hour listening to them at a pre-launch event. In fact, I listened to <em>10 </em>of them together. My first impressions? Quite positive, so allow me to share with you what I think of the Tower 5, Tower 7 and Tower Max. </p><h2 id="meet-the-ult-tower-max-tower-7-and-tower-5">Meet the ULT Tower Max, Tower 7 and Tower 5</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3000px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="ng8LAPWC4c8Sn4sniJbkAC" name="Sony_ULT_speakers-5" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/ng8LAPWC4c8Sn4sniJbkAC-1920-80.jpg" mos="" align="middle" fullscreen="" width="3000" height="1687" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>The Sony ULT Tower Max, Tower 7 and Tower 5 are bass-first party speakers that, according to Sony, deliver the most powerful bass yet in the brand's ULT Power Sound series. All three speakers are designed for music lovers, and they combine deep bass, high sound pressure, and party features for an unforgettable experience.</p><p>Regardless of the speaker you buy, you'll be getting XLR, RCA, USB-C and USB-A connectivity for hooking up mics for karaoke, DJ mixers, as well as instruments — making the ULT Towers perfect for nearly every occasion. Each speaker is also fitted with four handles and wheels for easy transport... though you may still need another pair of hands to help with the gargantuan Max.</p><p>The Tower Max, Tower 7 and Tower 5 are IPX4-rated so they're splash-proof. I'm surprised that they aren't more water-resistant, like the IP54-rated Marshall Bromley 750. That will limit the speakers' use outdoors — but really, you shouldn't be using electronic devices in the pouring rain anyway.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2566px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="gQ6MfgWD9B6XkqPcmXvquC" name="Sony_ULT_speakers-2" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/v2/w:1920,t:0,l:0,cw:2566,ch:1443,q:80/gQ6MfgWD9B6XkqPcmXvquC.png" mos="" align="middle" fullscreen="" width="2566" height="1446" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>Sony has worked with four acclaimed (and Grammy-nominated) hip-hop sound creators — Dot Da Genius, Daru Jones, DJ Dready, and Dennis "ROC.am" Jones — to fine-tune the ULT Towers' driver setup and sound profile. As a result, the speakers deliver distortion-free sound across the volume scale.</p><p>The Tower Max packs three 280mm woofers, two mid-range drivers, and four tweeters, so you're getting a lot of power. The Tower 7 is fitted with a single 280mm woofer, two mid-range drivers, and four tweeters, too. Lastly, the Tower 5 features a 230mm woofer, two mid-range drivers, and two tweeters. That's <em>a lot </em>for the lineup's smallest speaker!</p><p>You also get a customizable 10-band equalizer with all three speakers, and the new ULT button on the front grille now lets you swap between EQ presets. These include ULT1 (deep bass, low frequency sound), ULT2 (rumbling bass), Flat (true-to-source), and Live (spacious).</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:7070px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vfgsx9yAq73j7SRCCi4VBD" name="Sony_ULT_speakers-3" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/vfgsx9yAq73j7SRCCi4VBD-1920-80.jpg" mos="" align="middle" fullscreen="" width="7070" height="3977" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>There's really no dearth of features, which is excellent. And the ULT Towers sound like great value for money, too, as they aren't that expensive. I was personally quite surprised when the Sony representatives shared the retail prices with me. The Tower Max is the most expensive of the lot, at <a href="https://www.amazon.com/dp/B0HF5Z52X6" target="_blank" rel="nofollow">$1,499</a> / ~ £1,300; the Tower 7 is the mid-range option, at <a href="https://www.amazon.com/dp/B0HF6FGCZ3" target="_blank" rel="nofollow">$649</a> / ~ £600; and the Tower 5 is the cheapest option, at <a href="https://www.amazon.com/dp/B0HF67ZGC2" target="_blank" rel="nofollow">$449</a> / ~ £400.</p><h2 id="so-what-do-they-sound-like">So, what do they sound like?</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:1996px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="SrCNanAfRwUm7NzVb2UonB" name="Sony_ULT_speakers-6" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/SrCNanAfRwUm7NzVb2UonB-1920-80.jpg" mos="" align="middle" fullscreen="" width="1996" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>Driver sizes and specifications are all for naught if the speakers don't sound great — and in my short time with the Sony ULT Towers, I thought they sounded fantastic. At the pre-launch event, the Sony representatives played songs from different genres to show what the Tower Max, Tower 7 and Tower 5 were capable of.</p><p>Regardless of the song they played, I found myself grinning from ear to ear. All three speakers sounded beautiful. The bass sounded rich and meaty with plenty of low-end impact, and I thoroughly enjoyed the ULT2 EQ preset which made me feel the bass in my chest, temples and deep within my soul.</p><p>The Sony representatives played a song by Dua Lipa, too, and though I'm personally not a huge fan of hers, I couldn't help but tap my feet and bob my head along to the tune. I believe that's a true sign of a competent party speaker. If it can make you dance to artists you don't care for, it's done its job.</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:3000px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="qJxYLxmSggyBmSkTfBKafC" name="Sony_ULT_speakers-4" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/qJxYLxmSggyBmSkTfBKafC-1920-80.jpg" mos="" align="middle" fullscreen="" width="3000" height="1687" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>I also listened to two Tower 7s together in stereo mode with left and right channels. When 'Money' by Pink Floyd started playing and the sound of cash registers and coins traveled between the speakers, I was left extremely impressed. The high-pitched coins didn't sound like they were piercing through the soundstage. The treble sounded shiny and vibrant but not overzealous or fatiguing.</p><p>Finally, I listened to an electronic track on 10 ULT Towers all at once. It's not everyday you get to do something like that — how many of us have such extravagant setups in our basements, eh? The wall of speakers sounded phenomenal. The room-shaking bass made me feel like I was at a gig or a music festival.</p><h2 id="looking-ahead">Looking ahead...</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:7070px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vfgsx9yAq73j7SRCCi4VBD" name="Sony_ULT_speakers-3" alt="Sony ULT Tower party speakers" src="https://cdn.mos.cms.futurecdn.net/vfgsx9yAq73j7SRCCi4VBD-1920-80.jpg" mos="" align="middle" fullscreen="" width="7070" height="3977" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>So, Sony's latest generation of party speakers sounds excellent — on paper and in real-world use. The Tower Max, Tower 7 and Tower 5 offer something for everyone, and given how affordable they are (especially the Tower 5), they offer bang for your buck. That's based on my first impressions, of course, and it's subject to change when I do my in-depth testing.</p><p>Speaking of, I'm really looking forward to putting the Tower Max, Tower 7 and Tower 5 through their paces. Do these party speakers sound like they'd be up your street? Let me know in the comments below!</p><p><em>The Sony ULT Tower Max, Tower 7 and Tower 5 will be available to buy from Amazon U.S. and U.K., Best Buy, or </em><a href="http://sony.com/" target="_blank" rel="nofollow"><em>directly from Sony</em></a><em>.</em></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/audio/bluetooth-speakers/marshall-bromley-450-versus-marshall-bromley-750-face-off">I tested the Marshall Bromley 450 and the Marshall Bromley 750 party speakers — and the winner surprised me</a></li><li><a href="https://www.tomsguide.com/audio/bluetooth-speakers/forget-the-bose-s1-pro-the-brand-new-marshall-bromley-450-just-redefined-party-speakers">There's no better party speaker on the planet than the brand-new Marshall Bromley 450</a></li><li><a href="https://www.tomsguide.com/audio/bluetooth-speakers/as-a-guitarist-and-an-audiophile-this-is-the-all-in-one-speaker-ive-been-waiting-for">As a guitarist and an audiophile, this is the all-in-one speaker I’ve been waiting for</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Free TV on PS5: Sony just quietly launched a Pluto and Tubi competitor with 100+ live channels ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I'm a lot of things, and a cheapskate is one of them. Subscriptions are only getting more expensive, so I take every chance I can get to shout out whatever <a href="https://www.tomsguide.com/best-picks/best-free-streaming-services" target="_blank">the best free streaming services</a> have to offer. Now, Sony's launched a convenient <strong>new PlayStation 5 feature that adds over 100 free live channels. </strong></p><p>It's Sony's take on free ad-supported television like Tubi, Pluto TV and the Roku Channel, bringing fan-favorite TV shows like <strong>"Community"</strong> and <strong>"The Walking Dead" </strong>alongside sports programming from <strong>NFL Channel</strong>, <strong>UFC</strong>, and other staples, news, weather, long-running anime series from <strong>Crunchyroll</strong>, and more to the gaming console you already use. </p><p>(Btw if you're looking to snag a PS5 Pro to play GTA 6 on, check out our live blog on all <a href="https://www.tomsguide.com/gaming/playstation/ps5-pro-restocks-latest-updates" target="_blank">the latest PS5 Pro restocks</a>.)</p><p><strong>To watch these new free channels (you can find a full list below) now</strong>, just go to the <strong>Media tab</strong> on the console's home screen, <strong>select the Live TV on PS5 icon</strong>, and sign in to your PlayStation Account. Easy as that! Live TV on PS5 is currently available in the U.S. to start, and Sony plans to roll it out to Canada and other regions in due time. </p><p>Sony said the service will evolve over time, but at launch, here's all the free programming you can watch now:</p><h3 class="article-body__section" id="section-all-the-new-live-tv-channels-on-ps5"><span>All the new live TV channels on PS5</span></h3><ul><li><strong>Movies –</strong> Free movies across action, horror, sci-fi and more, with channels including MovieSphere by Lionsgate, 50 Cent Action, FilmRise Free Movies, Sony One Horror Hits and Cinevault.</li><li><strong>TV Shows –</strong> Fan-favorite programming including Community, The Shield, Hell’s Kitchen, The Walking Dead, Forensic Files, Unsolved Mysteries, Deal or No Deal USA and Fear Factor, plus Ice Road Truckers from A+E’s HISTORY.</li><li><strong>Anime – </strong>Anime favorites including Naruto, Sailor Moon, Hunter x Hunter, Death Note and more, including exclusive channels Black Clover and Box Sets channels from Crunchyroll.</li><li><strong>Sports – </strong>Sports programming from FOX Sports, NASCAR, NBC Sports Now, UFC and more spanning MMA, football, hockey, motorsports, golf, boxing and soccer</li><li><strong>News & Weather – </strong>National, local and international coverage including NBC News Now, Fox Weather, and LiveNOW from FOX</li><li><strong>Creator & Specialty Programming – </strong>Programming featuring Hot Ones, Spotify’s The Ringer, Futcrunch, Idle Mode, comedy, action sports, gaming, esports and other creator-led content.</li><li><strong>Additional Programming – </strong>Discover gaming and esports, creator-led entertainment, documentaries, podcasts and other specialty programming, including eCLUTCH (exclusive) and more.</li></ul><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="gdR2W4" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><p>It's the same FAST (Free Ad-Supported Streaming TV) channels that have been available for years on free streaming services like Tubi and Pluto TV, now just all in one convenient location on your PS5. Both live and on-demand content will be available at launch, and there's no paid subscription, hidden fees, or downloads required.</p><p>I'm always happy to see more free streaming options arrive on the scene since the days of streaming as a cheaper alternative to cable are long gone. Of the PS5's selling points, Live TV channels are barely a footnote, but it's a handy addition for those who already own a console and want to have access to live news, the latest seasons of their favorite TV shows, and sports all in one place.  So who else is gonna join me in watching the 50 Cent Action channel on PS5 tonight?</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-cable-replacements,review-4387.html">The best live TV streaming services in 2026: Cable TV alternatives that save you money</a></li><li><a href="https://www.tomsguide.com/entertainment/streaming/i-watched-hours-of-tubi-and-pluto-tv-to-see-which-free-streaming-service-has-more-ads-and-one-has-twice-as-many">I watched hours of Tubi and Pluto TV to see which free streaming service has more ads — and one has twice as many</a></li><li><a href="https://www.tomsguide.com/entertainment/streaming/3-free-shows-on-the-roku-channel-with-a-lot-of-seasons-including-a-nathan-fillion-mystery-drama">3 free shows on the Roku Channel with a lot of seasons — including a Nathan Fillion mystery drama</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/gaming/playstation/free-tv-on-ps5-sony-just-quietly-launched-a-pluto-and-tubi-competitor-with-100-live-channels</link>
                                                                            <description>
                            <![CDATA[ Sony just added over 100 live TV channels on PS5 for free — here's everything you can watch right now. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">RRMqkLB5y5659cEERi24gE</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/SGv7yeJNGJAioHnbNjRdVV-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 31 Aug 2026 20:59:52 +0000</pubDate>                                                                                                                                <updated>Mon, 07 Sep 2026 09:24:14 +0000</updated>
                                                                                                                                            <category><![CDATA[Playstation]]></category>
                                                    <category><![CDATA[Streaming]]></category>
                                                    <category><![CDATA[Gaming]]></category>
                                                    <category><![CDATA[Entertainment]]></category>
                                                                                                                    <dc:creator><![CDATA[ Alyse Stanley ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/BxNnQuBWRHqkv5xWZsjrjc-320-70.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/SGv7yeJNGJAioHnbNjRdVV-1920-80.jpg">
                                                            <media:credit><![CDATA[Sony]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of channels displayed alongside the text: Live TV on PS5 - Watch Something Great.]]></media:description>                                                            <media:text><![CDATA[A selection of channels displayed alongside the text: Live TV on PS5 - Watch Something Great.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of channels displayed alongside the text: Live TV on PS5 - Watch Something Great.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/SGv7yeJNGJAioHnbNjRdVV-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I'm a lot of things, and a cheapskate is one of them. Subscriptions are only getting more expensive, so I take every chance I can get to shout out whatever <a href="https://www.tomsguide.com/best-picks/best-free-streaming-services" target="_blank">the best free streaming services</a> have to offer. Now, Sony's launched a convenient <strong>new PlayStation 5 feature that adds over 100 free live channels. </strong></p><p>It's Sony's take on free ad-supported television like Tubi, Pluto TV and the Roku Channel, bringing fan-favorite TV shows like <strong>"Community"</strong> and <strong>"The Walking Dead" </strong>alongside sports programming from <strong>NFL Channel</strong>, <strong>UFC</strong>, and other staples, news, weather, long-running anime series from <strong>Crunchyroll</strong>, and more to the gaming console you already use. </p><p>(Btw if you're looking to snag a PS5 Pro to play GTA 6 on, check out our live blog on all <a href="https://www.tomsguide.com/gaming/playstation/ps5-pro-restocks-latest-updates" target="_blank">the latest PS5 Pro restocks</a>.)</p><p><strong>To watch these new free channels (you can find a full list below) now</strong>, just go to the <strong>Media tab</strong> on the console's home screen, <strong>select the Live TV on PS5 icon</strong>, and sign in to your PlayStation Account. Easy as that! Live TV on PS5 is currently available in the U.S. to start, and Sony plans to roll it out to Canada and other regions in due time. </p><p>Sony said the service will evolve over time, but at launch, here's all the free programming you can watch now:</p><h3 class="article-body__section" id="section-all-the-new-live-tv-channels-on-ps5"><span>All the new live TV channels on PS5</span></h3><ul><li><strong>Movies –</strong> Free movies across action, horror, sci-fi and more, with channels including MovieSphere by Lionsgate, 50 Cent Action, FilmRise Free Movies, Sony One Horror Hits and Cinevault.</li><li><strong>TV Shows –</strong> Fan-favorite programming including Community, The Shield, Hell’s Kitchen, The Walking Dead, Forensic Files, Unsolved Mysteries, Deal or No Deal USA and Fear Factor, plus Ice Road Truckers from A+E’s HISTORY.</li><li><strong>Anime – </strong>Anime favorites including Naruto, Sailor Moon, Hunter x Hunter, Death Note and more, including exclusive channels Black Clover and Box Sets channels from Crunchyroll.</li><li><strong>Sports – </strong>Sports programming from FOX Sports, NASCAR, NBC Sports Now, UFC and more spanning MMA, football, hockey, motorsports, golf, boxing and soccer</li><li><strong>News & Weather – </strong>National, local and international coverage including NBC News Now, Fox Weather, and LiveNOW from FOX</li><li><strong>Creator & Specialty Programming – </strong>Programming featuring Hot Ones, Spotify’s The Ringer, Futcrunch, Idle Mode, comedy, action sports, gaming, esports and other creator-led content.</li><li><strong>Additional Programming – </strong>Discover gaming and esports, creator-led entertainment, documentaries, podcasts and other specialty programming, including eCLUTCH (exclusive) and more.</li></ul><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="gdR2W4" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><p>It's the same FAST (Free Ad-Supported Streaming TV) channels that have been available for years on free streaming services like Tubi and Pluto TV, now just all in one convenient location on your PS5. Both live and on-demand content will be available at launch, and there's no paid subscription, hidden fees, or downloads required.</p><p>I'm always happy to see more free streaming options arrive on the scene since the days of streaming as a cheaper alternative to cable are long gone. Of the PS5's selling points, Live TV channels are barely a footnote, but it's a handy addition for those who already own a console and want to have access to live news, the latest seasons of their favorite TV shows, and sports all in one place.  So who else is gonna join me in watching the 50 Cent Action channel on PS5 tonight?</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-cable-replacements,review-4387.html">The best live TV streaming services in 2026: Cable TV alternatives that save you money</a></li><li><a href="https://www.tomsguide.com/entertainment/streaming/i-watched-hours-of-tubi-and-pluto-tv-to-see-which-free-streaming-service-has-more-ads-and-one-has-twice-as-many">I watched hours of Tubi and Pluto TV to see which free streaming service has more ads — and one has twice as many</a></li><li><a href="https://www.tomsguide.com/entertainment/streaming/3-free-shows-on-the-roku-channel-with-a-lot-of-seasons-including-a-nathan-fillion-mystery-drama">3 free shows on the Roku Channel with a lot of seasons — including a Nathan Fillion mystery drama</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested the Sony Bravia Theater Bar 7 + subwoofer — and recommending it is a no-brainer ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Sony Bravia Theater Bar 7 is Sony’s mid-range soundbar with true Dolby Atmos and DTS Virtual:X. If you’ve got a compatible Bravia TV, you even get access to IMAX Enhanced sound. Of course, it’s also a stellar “smart”(ish) home product, with Wi-Fi or Bluetooth streaming from various music streaming services. </p><p>As typical with the Theater Bar line, you can get the 7 with or without the subwoofer. I tested the model with the subwoofer. If you want a Sony Bravia experience for less than $1,099, you could check out the $349 Sony Bravia Theater Bar 5, but this isn’t compatible with Sony’s wireless subs. </p><p>With crystal-clear dialogue, powerful bass, and impressive surround sound, could the Bravia Theater Bar 7 be one of the <a href="https://www.tomsguide.com/us/best-soundbar-speakers,review-2004.html">best soundbars</a> of 2026? You bet. For the full story, keep reading this Sony Bravia Theater Bar 7 review. </p><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-specs"><span>Sony Bravia Theater Bar 7 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/Sony-BRAVIA-Theater-Soundbar-Speakers/dp/B0GSC13M1K" target="_blank" rel="nofollow">$869-$1,099</a> / <a href="https://www.amazon.co.uk/Sony-Theatre-Soundbar-Speaker-Surround/dp/B0GSXFKCJ4/" target="_blank" rel="nofollow">£699-£948</a></p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>HDMI 2.1, Bluetooth, Wi-Fi</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>Soundbar: 28.3 pounds / Subwoofer: 13.7 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>Soundbar: 5 x 37.5 x 2.6 inches / Subwoofer: 13.9 x 13.9 x 5.3 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black</p></td></tr><tr><td class="firstcol " ><p><strong>Subwoofer</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Dolby Atmos</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Channel</strong></p></td><td  ><p>5.1.2 (with sub)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-price-availability"><span>Sony Bravia Theater Bar 7 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="s83F4GUL5xZwgiG6PWsMNE" name="Sony Bravia Theater Bar 7 _ 4.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/s83F4GUL5xZwgiG6PWsMNE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony Bravia Theater Bar 7 + Sub is <a href="https://www.amazon.com/Sony-BRAVIA-Theater-Soundbar-Speakers/dp/B0GSC13M1K" target="_blank" rel="nofollow">$1,099 from Amazon U.S.</a> Without the sub, you’re looking at <a href="https://www.amazon.com/Sony-BRAVIA-Theater-Soundbar-Speakers/dp/B0GSBYNNX1" target="_blank" rel="nofollow">$869 from Amazon U.S.</a> I tested the subwoofer version, so that’s the model I’ll be discussing today.</p><p>In the U.K, the soundbar alone is <a href="https://www.amazon.co.uk/Sony-Theatre-Soundbar-Speaker-Surround/dp/B0GSXFKCJ4/" target="_blank" rel="nofollow">£699 from Amazon U.K.</a> You can’t buy the soundbar and subwoofer in one bundle in the U.K., but the overall price would be £948 — the sub is £249.</p><p>If you change your mind post-purchase and decide you want the subwoofer, you can buy it for <a href="https://www.amazon.com/Sony-Theater-Wireless-Subwoofer-Soundbar/dp/B0GSCFT5SG" target="_blank" rel="nofollow">$329</a>/£<a href="https://www.amazon.co.uk/Sony-BRAVIA-Theatre-Wireless-Subwoofer/dp/B0F24K6YDG" target="_blank" rel="nofollow">249</a> at Amazon. This subwoofer is compatible with: Bravia Theater 7, 8, and 9, as well as the Bravia Theater Trio and Quad, and the HT-A7000, HT-A5000, HT-A3000, and STR-AN1000 amps. Theoretically, you could get the Bravia Bar 7 and use the Subwoofer 9 ($898). Sony also offers the Theater Bar 5, 6, 8, and 9, which range from $349 to $1,498. </p><p>In comparison, this is a pretty average soundbar price. It’s not particularly good value or poor value. You can get the <a href="https://www.tomsguide.com/audio/soundbars/samsung-qs90h-review">Samsung QS90H</a>, an all-in-one soundbar (yes, including a built-in sub that’s actually powerful), for $999, or the <a href="https://www.tomsguide.com/audio/soundbars/bose-lifestyle-ultra-soundbar-hands-on-review-finally-a-real-sonos-arc-ultra-rival">Bose Lifestyle Ultra Soundbar</a> for $1,099. </p><p>If you want something a little cheaper but still good, I’d recommend the $449 <a href="https://www.tomsguide.com/audio/soundbars/polk-audio-signa-s4-review">Polk Audio Signa S4</a> or the $699 <a href="https://www.tomsguide.com/audio/soundbars/samsung-q600f-review">Samsung Q600F</a>. </p><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-design"><span>Sony Bravia Theater Bar 7 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="j5QAboXaM5xZJDn7L7kfgD" name="Sony Bravia Theater Bar 7 _ 9.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/j5QAboXaM5xZJDn7L7kfgD-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony Bravia Theater Bar 7 is relatively compact, making it ideal for TVs 55 inches and under. I tested it with a 55-inch LG G6, and it looked divine. Like most high-end soundbars, the Theater Bar 7 is effectively a long rectangle covered in a fine dark gray mesh. There’s no logo on the front, only a soft white light on the bottom right. No, you can’t tell it’s an expensive Sony soundbar from one glance, but you don’t buy a soundbar for the branding. You buy it for the sound. </p><p>Thankfully, as you’ll soon discover, it sounds great. </p><h2 id="controls">Controls</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="EXPL2DoBpfagMHFxmipJCE" name="Sony Bravia Theater Bar 7 _.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/EXPL2DoBpfagMHFxmipJCE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>In terms of controls, there’s a physical remote with a few basic commands. You can increase or decrease volume, turn it on and off, and toggle night, sound field, and voice modes. Of course, you can also control inputs. </p><p>This is effectively the same as the phone app, but the phone app, of course, has more advanced settings, which I’ll get into soon.</p><h2 id="features">Features</h2><p>Like Samsung soundbars, most of the Theater Bar 7’s USPs reside within compatible Bravia TVs. Unfortunately, I had to test with an LG G6 (boo-hoo me, I got to use one of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> of 2026, right?)</p><p>As any soundbar worth its salt should have, the Theater Bar 7 has a night mode and voice mode. Night mode reduces bass so you can watch blockbusters at 3am without receiving a noise complaint from your neighbors, while voice mode accentuates dialogue without making it sound unnatural. </p><p>There’s also a sound field mode. This effectively widens and raises the sound, which majorly improved my overall experience while watching movies.</p><p>The Bravia Connect app has a litany of advanced features, which I’ll dive into now. </p><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-app"><span>Sony Bravia Theater Bar 7 review: App</span></h2><p>The Theater Bar 7 uses Sony’s Bravia Connect app. While the app looks incredibly basic, almost bordering on careless, it boasts a range of advanced features. </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="NAVNsWoHjoMWxX6ujWwHVV" name="sony bravia theater bar 7" alt="three screenshots from the sony connect app showing room configuration and advanced audio settings menus" src="https://cdn.mos.cms.futurecdn.net/NAVNsWoHjoMWxX6ujWwHVV-1920-80.jpg" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide / Sony)</span></figcaption></figure><p>As you can see, there’s a room calibration setting, much like on the JBL Bar 1300X MK2 and the Samsung QS90H. This sends out a pulse of sound and calibrates the audio based on your watching space. </p><p>You can also adjust the sound field height. I set mine to the max, which benefited the flying scenes in “Top Gun: Maverick” massively. </p><p>Of course, you can also play music directly through Spotify or Qobuz Connect. You can also turn on DSEE Ultimate, an AI-assisted upscaler. If you’re an audio nerd, there’s no shortage of fun things to play with in Bravia Connect.</p><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-sound-quality"><span>Sony Bravia Theater Bar 7 review: Sound quality</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="hLs9LrE5LFWtBUDAqeg4yD" name="Sony Bravia Theater Bar 7 _ 7.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/hLs9LrE5LFWtBUDAqeg4yD-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I test every soundbar in the same room, with “Top Gun: Maverick” on 4K Blu-ray, various other Blu-rays, TV on basic Netflix, and music.</p><h2 id="movies">Movies</h2><p>As always, I started with “Top Gun: Maverick”. I customized the sound field height to max, upped the bass to 10, and turned on sound field and voice mode. The soundbar balanced in-scene sounds like jet engines with the soundtrack well. </p><p>However, some, but not all, high-frequency sounds were lost in the bass-heavy “Danger Zone” soundtrack. Despite this, louder details like whooshing steam and engine takeoff sounds were clear. Only the softer whipping of ropes melted into the background. </p><p>Even so, the bass on “Danger Zone” was excellent, and the vocals sauntered across the soundstage with unbelievable clarity. </p><p>In the first dog-fighting scene, I could feel the planes flying overhead and around me. The screeching jet engines rumbled through the soundbar without overpowering the soundtrack, which flitted underneath the fighter jets with grace. Even the radioed dialogue was perfectly audible. </p><p>Throughout the movie, I never struggled to hear dialogue, even in flying scenes. Every word was perfectly clear. Every note in the piano-led soundtrack twinkled across the soundscape like a beacon. Ocean waves and whistling wind sound effects were woven underneath dialogue beautifully. This was a fantastic viewing experience — just like the movie theater.  </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="JvNxyKfCyq7o5Z6Y66kPLE" name="Sony Bravia Theater Bar 7 _ 5.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/JvNxyKfCyq7o5Z6Y66kPLE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Next, I watched “Michael”. I needed to evaluate how the soundbar handled both dialogue and in-movie music beyond the soundtrack. Again, dialogue was crystal clear; perhaps this is TMI for some, but I could even make out the sound of saliva in the actors’ mouths. </p><p>The soundbar handled this movie excellently. Every performance was rich and immersive, with powerful yet controlled bass and exceptional clarity across the guitars, percussion, and vocals. Switching back to quiet, introspective scenes felt natural. </p><p>Finally, I watched “Oppenheimer”. The sound effects during the space dust clips (I’m not sure how else to describe them) glimmered and glitched in a way that made me feel like I was floating in the aether. The string-heavy soundtrack took control of the scenes and held tight, making me feel like I was also a physicist spinning out of control. </p><p>The softer detail sound effects like chirping birds and quacking ducks, as well as the crinkling of fabrics brushing together, also came through strongly. </p><h2 id="tv">TV</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="BdPE2iGC8GihhSmSpukBpD" name="Sony Bravia Theater Bar 7 _ 8.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/BdPE2iGC8GihhSmSpukBpD-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>To test TV performance, I watched “Interview with the Vampire” on basic Netflix, as you’re not always going to be watching 4K Blu-rays. </p><p>Even on basic Netflix, I could still hear soft in-scene sounds like the fluttering of wings or the subtle intake of breath. Dialogue, again, was clear and never overpowered the soundtrack. Action scenes, such as when Lestat transfixes his human prey in the speakeasy, were clear. </p><p>Diegetic (in-scene) sound effects like chirping crickets and crackling fires were audible over dialogue, no matter how softly spoken. </p><p>Overall, this performance was excellent. I really have no issues with this soundbar at all, except the <em>slight</em> muffling of high-frequency details in “Top Gun: Maverick”. </p><h2 id="music">Music</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="iv7zpY65iEDLFhQHpAUxrD" name="Sony Bravia Theater Bar 7 _ 6.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/iv7zpY65iEDLFhQHpAUxrD-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Music performance is okay. It’s neither the worst soundbar I’ve ever heard or the best — the best is still the Bose Smart Soundbar (2024). Vocals had a tendency to feel quite pinchy, and bass was strangely lackluster. But, I digress — soundbars aren’t made to play music. They’re designed to be the center of your home cinema.</p><p>I listened to “Knocking at the Sky” by The Last Dinner Party. While the soft “la la” notes right at the end sounded great, the bombastic chorus was muddled and the vocals pinched in my ears. There was a disappointing amount of tinniness in the percussion, too. </p><p>Electronic music fared a little better. “If you wanna party, come over to my house (Eli Escobar remix)” by Fcukers was clearer and didn’t hurt my ears. The pulsing synth and warbling synth details were both full-bodied and confident, but the bass synth fell flat for me. There just wasn’t any intensity, likely due to the lack of bass.</p><p>Lastly, I switched to something heavier: “Red40” by Die Spitz. This guitar-led metal-punk-indie track is usually propulsive and insistent, but the vocals were squashed into the guitars and the kick drum didn’t carry any weight. </p><p>I was disappointed by this music performance because of how utterly excellent the movie and TV experience was, but you can’t win them all, and this is a soundbar — not a speaker. </p><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-verdict"><span>Sony Bravia Theater Bar 7 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="R22rbBhqHpLoTD4AYbR7QE" name="Sony Bravia Theater Bar 7 _ 3.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/R22rbBhqHpLoTD4AYbR7QE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony Bravia Theater Bar 7 is a fantastic soundbar. With the subwoofer, you get powerful, controlled, and immersive bass, and the soundbar itself has a knack for delivering crystal-clear dialogue balanced perfectly over top of a dramatic OST. In terms of movie and TV performance, I really have nothing bad to say. Other than the <em>tiny</em> suppression of some high-frequency sounds in “Top Gun: Maverick”, the Bar 7 gets applause all round from me. </p><p>Sure, the app could look a little nicer — it’s nothing compared to the aesthetic of SmartThings — but are you going to be spending that much time simply looking at it? It works well and is reliable, with zero connectivity issues, and for me, that’s enough. </p><p>Whether you buy the subwoofer or not, I’m sure you’ll be incredibly pleased with the Theater Bar 7. It’s reliable, powerful, and immersive. For me, that’s all I want from a soundbar. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/soundbars/sony-bravia-theater-bar-7-review</link>
                                                                            <description>
                            <![CDATA[ The Sony Bravia Theater Bar 7 + subwoofer is an instant home theater upgrade, with crystal-clear dialogue, impressive bass, and music streaming. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">sdnkBh9Qm7shNhwQV22TWJ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/BVvaP4fjYAp5K2ccWcZ3QE-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 29 Aug 2026 05:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Soundbars]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Speakers]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/BVvaP4fjYAp5K2ccWcZ3QE-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom&#039;s guide blue background]]></media:description>                                                            <media:text><![CDATA[the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom&#039;s guide blue background]]></media:text>
                                <media:title type="plain"><![CDATA[the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom&#039;s guide blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/BVvaP4fjYAp5K2ccWcZ3QE-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Sony Bravia Theater Bar 7 is Sony’s mid-range soundbar with true Dolby Atmos and DTS Virtual:X. If you’ve got a compatible Bravia TV, you even get access to IMAX Enhanced sound. Of course, it’s also a stellar “smart”(ish) home product, with Wi-Fi or Bluetooth streaming from various music streaming services. </p><p>As typical with the Theater Bar line, you can get the 7 with or without the subwoofer. I tested the model with the subwoofer. If you want a Sony Bravia experience for less than $1,099, you could check out the $349 Sony Bravia Theater Bar 5, but this isn’t compatible with Sony’s wireless subs. </p><p>With crystal-clear dialogue, powerful bass, and impressive surround sound, could the Bravia Theater Bar 7 be one of the <a href="https://www.tomsguide.com/us/best-soundbar-speakers,review-2004.html">best soundbars</a> of 2026? You bet. For the full story, keep reading this Sony Bravia Theater Bar 7 review. </p><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-specs"><span>Sony Bravia Theater Bar 7 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/Sony-BRAVIA-Theater-Soundbar-Speakers/dp/B0GSC13M1K" target="_blank" rel="nofollow">$869-$1,099</a> / <a href="https://www.amazon.co.uk/Sony-Theatre-Soundbar-Speaker-Surround/dp/B0GSXFKCJ4/" target="_blank" rel="nofollow">£699-£948</a></p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>HDMI 2.1, Bluetooth, Wi-Fi</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>Soundbar: 28.3 pounds / Subwoofer: 13.7 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>Soundbar: 5 x 37.5 x 2.6 inches / Subwoofer: 13.9 x 13.9 x 5.3 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black</p></td></tr><tr><td class="firstcol " ><p><strong>Subwoofer</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Dolby Atmos</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Channel</strong></p></td><td  ><p>5.1.2 (with sub)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-price-availability"><span>Sony Bravia Theater Bar 7 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="s83F4GUL5xZwgiG6PWsMNE" name="Sony Bravia Theater Bar 7 _ 4.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/s83F4GUL5xZwgiG6PWsMNE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony Bravia Theater Bar 7 + Sub is <a href="https://www.amazon.com/Sony-BRAVIA-Theater-Soundbar-Speakers/dp/B0GSC13M1K" target="_blank" rel="nofollow">$1,099 from Amazon U.S.</a> Without the sub, you’re looking at <a href="https://www.amazon.com/Sony-BRAVIA-Theater-Soundbar-Speakers/dp/B0GSBYNNX1" target="_blank" rel="nofollow">$869 from Amazon U.S.</a> I tested the subwoofer version, so that’s the model I’ll be discussing today.</p><p>In the U.K, the soundbar alone is <a href="https://www.amazon.co.uk/Sony-Theatre-Soundbar-Speaker-Surround/dp/B0GSXFKCJ4/" target="_blank" rel="nofollow">£699 from Amazon U.K.</a> You can’t buy the soundbar and subwoofer in one bundle in the U.K., but the overall price would be £948 — the sub is £249.</p><p>If you change your mind post-purchase and decide you want the subwoofer, you can buy it for <a href="https://www.amazon.com/Sony-Theater-Wireless-Subwoofer-Soundbar/dp/B0GSCFT5SG" target="_blank" rel="nofollow">$329</a>/£<a href="https://www.amazon.co.uk/Sony-BRAVIA-Theatre-Wireless-Subwoofer/dp/B0F24K6YDG" target="_blank" rel="nofollow">249</a> at Amazon. This subwoofer is compatible with: Bravia Theater 7, 8, and 9, as well as the Bravia Theater Trio and Quad, and the HT-A7000, HT-A5000, HT-A3000, and STR-AN1000 amps. Theoretically, you could get the Bravia Bar 7 and use the Subwoofer 9 ($898). Sony also offers the Theater Bar 5, 6, 8, and 9, which range from $349 to $1,498. </p><p>In comparison, this is a pretty average soundbar price. It’s not particularly good value or poor value. You can get the <a href="https://www.tomsguide.com/audio/soundbars/samsung-qs90h-review">Samsung QS90H</a>, an all-in-one soundbar (yes, including a built-in sub that’s actually powerful), for $999, or the <a href="https://www.tomsguide.com/audio/soundbars/bose-lifestyle-ultra-soundbar-hands-on-review-finally-a-real-sonos-arc-ultra-rival">Bose Lifestyle Ultra Soundbar</a> for $1,099. </p><p>If you want something a little cheaper but still good, I’d recommend the $449 <a href="https://www.tomsguide.com/audio/soundbars/polk-audio-signa-s4-review">Polk Audio Signa S4</a> or the $699 <a href="https://www.tomsguide.com/audio/soundbars/samsung-q600f-review">Samsung Q600F</a>. </p><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-design"><span>Sony Bravia Theater Bar 7 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="j5QAboXaM5xZJDn7L7kfgD" name="Sony Bravia Theater Bar 7 _ 9.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/j5QAboXaM5xZJDn7L7kfgD-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony Bravia Theater Bar 7 is relatively compact, making it ideal for TVs 55 inches and under. I tested it with a 55-inch LG G6, and it looked divine. Like most high-end soundbars, the Theater Bar 7 is effectively a long rectangle covered in a fine dark gray mesh. There’s no logo on the front, only a soft white light on the bottom right. No, you can’t tell it’s an expensive Sony soundbar from one glance, but you don’t buy a soundbar for the branding. You buy it for the sound. </p><p>Thankfully, as you’ll soon discover, it sounds great. </p><h2 id="controls">Controls</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="EXPL2DoBpfagMHFxmipJCE" name="Sony Bravia Theater Bar 7 _.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/EXPL2DoBpfagMHFxmipJCE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>In terms of controls, there’s a physical remote with a few basic commands. You can increase or decrease volume, turn it on and off, and toggle night, sound field, and voice modes. Of course, you can also control inputs. </p><p>This is effectively the same as the phone app, but the phone app, of course, has more advanced settings, which I’ll get into soon.</p><h2 id="features">Features</h2><p>Like Samsung soundbars, most of the Theater Bar 7’s USPs reside within compatible Bravia TVs. Unfortunately, I had to test with an LG G6 (boo-hoo me, I got to use one of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> of 2026, right?)</p><p>As any soundbar worth its salt should have, the Theater Bar 7 has a night mode and voice mode. Night mode reduces bass so you can watch blockbusters at 3am without receiving a noise complaint from your neighbors, while voice mode accentuates dialogue without making it sound unnatural. </p><p>There’s also a sound field mode. This effectively widens and raises the sound, which majorly improved my overall experience while watching movies.</p><p>The Bravia Connect app has a litany of advanced features, which I’ll dive into now. </p><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-app"><span>Sony Bravia Theater Bar 7 review: App</span></h2><p>The Theater Bar 7 uses Sony’s Bravia Connect app. While the app looks incredibly basic, almost bordering on careless, it boasts a range of advanced features. </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="NAVNsWoHjoMWxX6ujWwHVV" name="sony bravia theater bar 7" alt="three screenshots from the sony connect app showing room configuration and advanced audio settings menus" src="https://cdn.mos.cms.futurecdn.net/NAVNsWoHjoMWxX6ujWwHVV-1920-80.jpg" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide / Sony)</span></figcaption></figure><p>As you can see, there’s a room calibration setting, much like on the JBL Bar 1300X MK2 and the Samsung QS90H. This sends out a pulse of sound and calibrates the audio based on your watching space. </p><p>You can also adjust the sound field height. I set mine to the max, which benefited the flying scenes in “Top Gun: Maverick” massively. </p><p>Of course, you can also play music directly through Spotify or Qobuz Connect. You can also turn on DSEE Ultimate, an AI-assisted upscaler. If you’re an audio nerd, there’s no shortage of fun things to play with in Bravia Connect.</p><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-sound-quality"><span>Sony Bravia Theater Bar 7 review: Sound quality</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="hLs9LrE5LFWtBUDAqeg4yD" name="Sony Bravia Theater Bar 7 _ 7.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/hLs9LrE5LFWtBUDAqeg4yD-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I test every soundbar in the same room, with “Top Gun: Maverick” on 4K Blu-ray, various other Blu-rays, TV on basic Netflix, and music.</p><h2 id="movies">Movies</h2><p>As always, I started with “Top Gun: Maverick”. I customized the sound field height to max, upped the bass to 10, and turned on sound field and voice mode. The soundbar balanced in-scene sounds like jet engines with the soundtrack well. </p><p>However, some, but not all, high-frequency sounds were lost in the bass-heavy “Danger Zone” soundtrack. Despite this, louder details like whooshing steam and engine takeoff sounds were clear. Only the softer whipping of ropes melted into the background. </p><p>Even so, the bass on “Danger Zone” was excellent, and the vocals sauntered across the soundstage with unbelievable clarity. </p><p>In the first dog-fighting scene, I could feel the planes flying overhead and around me. The screeching jet engines rumbled through the soundbar without overpowering the soundtrack, which flitted underneath the fighter jets with grace. Even the radioed dialogue was perfectly audible. </p><p>Throughout the movie, I never struggled to hear dialogue, even in flying scenes. Every word was perfectly clear. Every note in the piano-led soundtrack twinkled across the soundscape like a beacon. Ocean waves and whistling wind sound effects were woven underneath dialogue beautifully. This was a fantastic viewing experience — just like the movie theater.  </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="JvNxyKfCyq7o5Z6Y66kPLE" name="Sony Bravia Theater Bar 7 _ 5.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/JvNxyKfCyq7o5Z6Y66kPLE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Next, I watched “Michael”. I needed to evaluate how the soundbar handled both dialogue and in-movie music beyond the soundtrack. Again, dialogue was crystal clear; perhaps this is TMI for some, but I could even make out the sound of saliva in the actors’ mouths. </p><p>The soundbar handled this movie excellently. Every performance was rich and immersive, with powerful yet controlled bass and exceptional clarity across the guitars, percussion, and vocals. Switching back to quiet, introspective scenes felt natural. </p><p>Finally, I watched “Oppenheimer”. The sound effects during the space dust clips (I’m not sure how else to describe them) glimmered and glitched in a way that made me feel like I was floating in the aether. The string-heavy soundtrack took control of the scenes and held tight, making me feel like I was also a physicist spinning out of control. </p><p>The softer detail sound effects like chirping birds and quacking ducks, as well as the crinkling of fabrics brushing together, also came through strongly. </p><h2 id="tv">TV</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="BdPE2iGC8GihhSmSpukBpD" name="Sony Bravia Theater Bar 7 _ 8.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/BdPE2iGC8GihhSmSpukBpD-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>To test TV performance, I watched “Interview with the Vampire” on basic Netflix, as you’re not always going to be watching 4K Blu-rays. </p><p>Even on basic Netflix, I could still hear soft in-scene sounds like the fluttering of wings or the subtle intake of breath. Dialogue, again, was clear and never overpowered the soundtrack. Action scenes, such as when Lestat transfixes his human prey in the speakeasy, were clear. </p><p>Diegetic (in-scene) sound effects like chirping crickets and crackling fires were audible over dialogue, no matter how softly spoken. </p><p>Overall, this performance was excellent. I really have no issues with this soundbar at all, except the <em>slight</em> muffling of high-frequency details in “Top Gun: Maverick”. </p><h2 id="music">Music</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="iv7zpY65iEDLFhQHpAUxrD" name="Sony Bravia Theater Bar 7 _ 6.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/iv7zpY65iEDLFhQHpAUxrD-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Music performance is okay. It’s neither the worst soundbar I’ve ever heard or the best — the best is still the Bose Smart Soundbar (2024). Vocals had a tendency to feel quite pinchy, and bass was strangely lackluster. But, I digress — soundbars aren’t made to play music. They’re designed to be the center of your home cinema.</p><p>I listened to “Knocking at the Sky” by The Last Dinner Party. While the soft “la la” notes right at the end sounded great, the bombastic chorus was muddled and the vocals pinched in my ears. There was a disappointing amount of tinniness in the percussion, too. </p><p>Electronic music fared a little better. “If you wanna party, come over to my house (Eli Escobar remix)” by Fcukers was clearer and didn’t hurt my ears. The pulsing synth and warbling synth details were both full-bodied and confident, but the bass synth fell flat for me. There just wasn’t any intensity, likely due to the lack of bass.</p><p>Lastly, I switched to something heavier: “Red40” by Die Spitz. This guitar-led metal-punk-indie track is usually propulsive and insistent, but the vocals were squashed into the guitars and the kick drum didn’t carry any weight. </p><p>I was disappointed by this music performance because of how utterly excellent the movie and TV experience was, but you can’t win them all, and this is a soundbar — not a speaker. </p><h2 class="article-body__section" id="section-sony-bravia-theater-bar-7-review-verdict"><span>Sony Bravia Theater Bar 7 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="R22rbBhqHpLoTD4AYbR7QE" name="Sony Bravia Theater Bar 7 _ 3.JPG" alt="the sony bravia theater bar 7 photographed with the subwoofer with a TV on the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/R22rbBhqHpLoTD4AYbR7QE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony Bravia Theater Bar 7 is a fantastic soundbar. With the subwoofer, you get powerful, controlled, and immersive bass, and the soundbar itself has a knack for delivering crystal-clear dialogue balanced perfectly over top of a dramatic OST. In terms of movie and TV performance, I really have nothing bad to say. Other than the <em>tiny</em> suppression of some high-frequency sounds in “Top Gun: Maverick”, the Bar 7 gets applause all round from me. </p><p>Sure, the app could look a little nicer — it’s nothing compared to the aesthetic of SmartThings — but are you going to be spending that much time simply looking at it? It works well and is reliable, with zero connectivity issues, and for me, that’s enough. </p><p>Whether you buy the subwoofer or not, I’m sure you’ll be incredibly pleased with the Theater Bar 7. It’s reliable, powerful, and immersive. For me, that’s all I want from a soundbar. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve taken over 3,000 photos on the Sony RX10 V — its 25x zoom lens and 30fps blackout-free burst make it the king of bridge cameras ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The king of bridge cameras returns. Nearly a decade since the fourth-gen camera came out, Sony has introduced substantial improvements and upgrades to make the RX10 V a simply outstanding camera. With better ergonomics and controls, and bigger battery life, the RX10 V feels more like a mirrorless Alpha camera — and I’m not complaining.</p><p>Handling the RX10 V is a joy, and its long-reaching 24-600mm (full frame equivalent) lens lets you get up, close and personal with your subjects. Thanks to 30fps blackout-free drive and excellent autofocus, this is a solid camera for amateur wildlife and sports photographers. The 20.1MP images boast beautiful color reproduction, too.</p><p>Though not without its flaws, there aren’t many reasons to not buy this camera. To find out if this is the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best camera </a>for you, read my full Sony RX10 V review.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-specs"><span>Sony RX10 V review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.adorama.com/sony-dsc-rx10-v-digital-camera/p/isorx10m5" target="_blank" rel="nofollow">$2,298</a> / <a href="https://www.lcegroup.co.uk/New/Sony-RX10-V-Digital-Camera_234898.html" target="_blank" rel="nofollow">£2,199</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>20.1MP 1-inch Stacked Exmor RS CMOS</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>BIONZ XR</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Optical SteadyShot</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Fast Hybrid AF (phase-detection AF + contrast-detection AF)</p></td></tr><tr><td class="firstcol " ><p><strong>Focus modes</strong></p></td><td  ><p>Single Shot, Continuous AF, DMF, Manual focus</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>0.5-inch Quad-VGA OLED EVF, approx. 3.68 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>3-inch TFT LCD touchscreen, approx. 1.6 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>ISO range</strong></p></td><td  ><p>64-12,800</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>4K/60fps | 4K/120fps | FHD/60fps</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>USB-C, micro HDMI, USB Micro-B 2.0, 3.5mm mic in, 3.5mm headphones out, 1x SD/SDHC/SDXC UHS-I/II</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless/bluetooth</strong></p></td><td  ><p>Wi-Fi, Bluetooth</p></td></tr><tr><td class="firstcol " ><p><strong>Max shooting speed</strong></p></td><td  ><p>30fps (electronic) | 10fps (mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Max shutter speed</strong></p></td><td  ><p>30s to 1/16,000s (stills, electronic) | 30s to 1/2,000s (stills, mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life (Stills, CIPA)</strong></p></td><td  ><p>630 shots (LCD) | 570 shots (EVF)</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>5.37 x 3.72 x 5.96 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.7lbs (with battery and card)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sony-rx10-v-review-price-availability"><span>Sony RX10 V 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="D7njNQMomC9joTYj4eczBd" name="Sony_RX10_ 1.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/D7njNQMomC9joTYj4eczBd-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V has been nearly a decade in the making, and it succeeds the RX10 IV which first came out in 2017. The RX10 V is a bridge camera that literally bridges the gap between basic point-and-shoot compacts and full frame cameras, and it borrows features from Sony’s Alpha lineup (more on that shortly). It retails for <a href="https://www.adorama.com/sony-dsc-rx10-v-digital-camera/p/isorx10m5" target="_blank" rel="nofollow">$2,298 at Adorama U.S.</a> / <a href="https://www.lcegroup.co.uk/New/Sony-RX10-V-Digital-Camera_234898.html" target="_blank" rel="nofollow">£2,199 at London Camera Exchange U.K.</a></p><p>It’s pricier than the <a href="https://www.tomsguide.com/cameras-photography/nikon-coolpix-p1100-review">Nikon Coolpix P1100</a> ($1,146) and the Panasonic Lumix DMC-FZ2000/FZ2500 ($1,049) but it’s worth the extra spend if you want 30fps blackout-free burst shooting, fantastic autofocus, 4K/60fps video, and long battery life.</p><p>Spending nearly $3,000 on a camera may sound like a lot. But given that the camera features a built-in 24-600mm (full frame equivalent) lens, it negates the need to drop nearly a grand or more on a lens. This softens the blows a bit — especially because a telephoto lens on its own can set you back serious money. For context, a Sony 400-800mm lens costs over $3,000, while Canon’s RF1200mm lens costs well over $22,000.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-design"><span>Sony RX10 V 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="D5jR7azS4uEABT7YnzZQ9d" name="Sony_RX10_ 3.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/D5jR7azS4uEABT7YnzZQ9d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Bridge cameras offer the convenience of point-and-shoot compacts and stuff it into DSLR-esque bodies. It’s no surprise, then, that the Sony RX10 V is slightly bulky, measuring 5.37 x 3.72 x 5.96 inches and weighing 2.7lbs (with battery and cards). It’s certainly bigger than my trusty mirrorless <a href="https://www.tomsguide.com/cameras-photography/fujifilm-x-t50-review">Fujifilm X-T50</a> ($1,399) but even so, it’s more compact and lighter than the Nikon Coolpix P1100.</p><p>Unlike the Nikon Coolpix P1100, the RX10 V doesn’t feel as unwieldy to handle, thanks to its spacious protruding grip, which is deep and provides plenty of space for your fingers to wrap around it. Compared to the RX10 IV, the fifth-gen model offers better ergonomics, from its deeper grip to its more prominent command dials. The RX10 V feels like a modern Alpha camera, like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-spent-a-day-shooting-on-the-new-sony-a7-v-here-are-my-first-impressions-spoiler-alert-its-great">Sony A7 V</a> ($2,899). Build quality feels solid, too, and I’d expect nothing less from Sony. </p><h2 id="viewfinder">Viewfinder</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:879px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="Tk6ZE7ercUexT3wNHJ2X7d" name="Sony_RX10_ 7.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/v2/w:1920,t:43,l:716,cw:879,ch:494,q:80/Tk6ZE7ercUexT3wNHJ2X7d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The RX10 V’s viewfinder has been upgraded. While its predecessor featured a 0.39-inch XGA OLED electronic viewfinder (EVF) with 0.7x magnification, the RX10 V packs a 0.5-inch Quad-VGA OLED EVF with 0.78x magnification — similar to the one on the flagship <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">Sony A1 II </a>($6,499).</p><p>The resolution has received a bump, too, from 2.35 million dots on the RX10 IV to 3.68 million dots on the RX10 V. You now get a sharp, detailed, life-like representation of the scene you’re capturing. This helps you accurately frame and capture your subject. The RX10 V’s EVF resolution is similar to the Sony A7 V’s (3.69M dots), the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review">Canon EOS R6 Mark III</a>’s (3.69M dots, $2,799) and the <a href="https://www.tomsguide.com/reviews/panasonic-lumix-g9-ii">Panasonic Lumix G9II</a>’s (3.68M dots, $1,899), though it’s slightly less than the Nikon Coolpix P1100’s (2.36M dots).</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:7365px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kJeKaFvJfxFCK8di2AFVra" name="Sony_RX10V" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/kJeKaFvJfxFCK8di2AFVra-1920-80.jpg" mos="" align="middle" fullscreen="" width="7365" height="4143" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>I’ve found the RX10 V’s viewfinder extremely comfortable to look through, even through my prescription glasses. I also love that its refresh rate can be set to either 60Hz or 120Hz — the same as on the Canon EOS R6 Mark III and the much pricier <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r5-mark-ii-review">Canon EOS R5 Mark II</a> ($4,299). 120Hz refresh rate ensures that the subjects you’re tracking remain free of motion blur, and the RX10 V delivers smooth and realistic motion, making it great for fast-paced sports and wildlife photography.</p><p>The RX10 V borrows another feature from the Sony A7 V, and that’s 30fps blackout-free burst shooting. This means that there are no black screens between shots, enabling you to keep a constant view of your subject as you shoot. This is something we’ve also seen on other wildlife-oriented cameras, like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-review">OM System OM-1 Mark II</a> ($2,399), and the RX10 V wins brownie points over the Canon EOS R6 Mark III here which doesn’t feature blackout-free sequential shooting.</p><h2 id="monitor">Monitor</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="Tk6ZE7ercUexT3wNHJ2X7d" name="Sony_RX10_ 7.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/Tk6ZE7ercUexT3wNHJ2X7d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The RX10 V’s rear monitor has received a modest upgrade, too. While the screen size remains the same — 3-inch on both the RX10 IV and the RX10 V — the touchscreen now has a resolution of 1.6 million dots. The 3:2 aspect ratio also matches the camera’s sensor now, as opposed to the 4:3 aspect ratio on the RX10 V’s predecessor.</p><p>I’ve found the RX10 V’s monitor highly responsive in my testing, and I’ve had no trouble viewing it in different lighting conditions, including direct sunlight. It’s also much brighter than the Nikon Coolpix P1100’s 921K-dot monitor. The RX10 V’s screen can tilt up and down but it can’t hinge or tilt sideways, like the Nikon Coolpix P1100’s can, though, which limits the extent of shooting at awkward, low-to-the-ground or overhead angles.</p><h2 id="ports">Ports</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="abHg4KmiWHT4D5i2uHMp6d" name="Sony_RX10_ 4.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/abHg4KmiWHT4D5i2uHMp6d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>All the ports are located on the RX10 V’s left edge and are protected by plastic flaps. You get a USB-C port for charging and data transfer; a legacy USB Micro-B port for accessories; a micro HDMI port; and dual 3.5mm ports for mic in and headphones out. The memory card slot is located next to the battery pack on the bottom plate.The RX10 V is compatible with a single SD/SDHC/SDXC UHS-I/II card.</p><h2 id="weather-sealing">Weather-sealing</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="7ytCwpXxA3Gc7jmJpscjCd" name="Sony_RX10_ 5.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/7ytCwpXxA3Gc7jmJpscjCd-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Unlike the Nikon Coolpix P1100, the RX10 V is fully weather-sealed and protected against moisture and dust. This gives you more confidence when shooting outdoors and in changeable weather conditions.</p><p>The RX10 V’s weather-sealing comes at a cost, though — but not a huge one. There’s no built-in flash, like on previous iterations of the line, but a small pop-up flash is rarely powerful enough to light up distant subjects (perhaps macro subjects, yes), so it’s not something you’ll necessarily miss.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-controls"><span>Sony RX10 V review: Controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9RS5FmpcSe7aHkAiAmGy4d" name="Sony_RX10_ 11.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/9RS5FmpcSe7aHkAiAmGy4d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V is a delight to use, thanks to its well-placed and large buttons and dials. On the RX10 IV, an LCD screen took up a fair bit of space on the top plate, but this has been replaced by a prominent command dial that lets you swap between shooting modes as well as stills, video, and slow-and-quick (S&Q) video. You now get additional dials for shutter speed and ISO, too.</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="wFs93caYnHmcFtxuY3Ud9d" name="Sony_RX10_ 13.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/wFs93caYnHmcFtxuY3Ud9d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I like that the dual custom (C1 and C2) buttons have been moved up and are now located next to the shutter button. This makes accessing them much easier, and it makes shooting feel more intuitive. Sony has also added a physical focus joystick, similar to the one on its latest Alpha models, on the rear panel — something that was absent on the RX10 IV. The navigation wheel is bigger, too, and again, feels like it’s been borrowed from the A7 V or the A1 II.</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="5C4qbvPkai7F8T7xRJzg6d" name="Sony_RX10_ 10.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/5C4qbvPkai7F8T7xRJzg6d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>These quality of life improvements result not only in better ergonomics, but also in a more well-rounded user experience. My only complaint is that the RX10 V’s three dials are only accessible by your right thumb, and I would have much preferred having a front dial for adjusting, say, shutter speed. Maybe sixth time’s the charm, eh, Sony?</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="35J5e8aib7AhbiUV38Yy6d" name="Sony_RX10_ 9.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/35J5e8aib7AhbiUV38Yy6d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>A decade is a long time, and it isn’t surprising that the RX10 IV’s menu system has been overhauled to deliver a more user-friendly experience on the RX 10 V. Instead of being text-heavy, the RX10 V’s menu system is color-coded, feels more logical and less cluttered, and it’s clearly been modeled after recent Alpha cameras.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-autofocus"><span>Sony RX10 V review: Autofocus</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="nkcaZDv8EKT9uRjAoukS8d" name="Sony_RX10_ 6.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/nkcaZDv8EKT9uRjAoukS8d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V features an advanced hybrid autofocus system which uses a combination of phase- and contrast-detection AF, driven by the BIONZ XR processor. It inherits the Sony A7 V’s AI chip with real-time subject detection autofocus, and the Sony A1 II’s Auto Subject Detection which automatically swaps between human, bird/animal, insect, and vehicle detection modes.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/JFE9mn8hPpRNXpxfzF8wca-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/iRW75EF84nSRu3G2P2wW4d-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fVjajBpMmn5KoRLSdfW2mc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/8fmPW9UNj8EPUqrfaZYmBd-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/bFDXmBF9nrRLzKcQj3rTKa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/4CpeZt9uE28bQJxuwVeoJb-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Regardless of the subject I was photographing, the RX10 V didn’t let me down. As you can see in the gallery above, the camera was quick to find and lock on to birds, humans, and insects. When I was photographing birds and insects at a lake, I appreciated the camera’s Auto Subject Detection, and I found it very reliable. I was able to get the shots I needed as I didn’t have to waste time swapping between subjects manually.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/LXnxDGR9jCmMCNgcBzuaTc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/rr3xKP6dgojmVm3NVCwnaZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The photos above aren’t my finest work, but I wanted to use these two photos to demonstrate how well the RX10 V’s autofocus works. Both the squirrel and bird were obscured by leaves, and because of very bright sunlight, I couldn’t focus on them with my own eyes — so I pointed my camera right overhead and the RX10 V immediately locked on to them. <em>That’s </em>how smart the camera’s AF system is.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/4ptzqxEicPqDN7Fx5Tgvec-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/uAEw5jkaU6BDFKBxondJAd-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/euKhgZ5EbrzNd6JcUwPLcc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9bZFMauzQ4t7LDrmLo8LYc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I also appreciated the RX10 V locking on to and tracking the black-headed gull as I fired off a burst. The autofocus didn’t break at all, even as the gull landed on the water, surrounded by other birds. As you can see in the gallery above, it’s still in focus in the last two shots. </p><h2 class="article-body__section" id="section-sony-rx10-v-review-stabilization"><span>Sony RX10 V review: Stabilization</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/iR74apVdMuhtB9pboSqCyX-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 1/3s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MLd3MR7SrVTM6ktQgxZwUX-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 1/2s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V utilizes Sony’s SteadyShot optical image stabilization which is also found in the <a href="https://www.tomsguide.com/cameras-photography/sony-zv-e10-ii-review">Sony ZV-E10 II</a> ($999) and the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-rx1r-iii-review">Sony RX1R III</a> ($5,099), and it incurs a crop. The lack of in-body image stabilization (IBIS) means you can’t shoot at super slow shutter speeds, but it should just about suffice when shooting handheld.</p><p>In my testing, I was able to shoot handheld at shutter speeds as slow as 1/3s (first image above) but any slower than that, like 1/2s (second image above), and the resulting image was blurry, impacted by camera shake. This is still good, though!</p><h2 class="article-body__section" id="section-sony-rx10-v-review-image-quality"><span>Sony RX10 V review: Image quality</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:4867px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="9g4U23oDsUQ9m8i9Fn36ca" name="Sony_RX10V-2" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/9g4U23oDsUQ9m8i9Fn36ca-1920-80.jpg" mos="" align="middle" fullscreen="" width="4867" height="2738" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V packs a small 1-inch Stacked Exmor RS CMOS sensor with a 20.1MP resolution. Though small, its sensor is still bigger than the Nikon Coolpix P1100’s 1/2.3-inch 16MP one. You can’t make extreme crops as you can on, say, the 33MP full frame Sony A7 V, but given that you get a 24-600mm effective focal length, you can still capture detailed images of distant subjects.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/aKy3htXcWfzsPYo6ayoiic-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/NjLg2nEStFVDRLaPk4cVoc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 24mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Before we dive into image quality, let’s talk about the RX10 V’s reach. The RX10 V is fitted with a ZEISS Vario-Sonnar T 24-600mm lens, and this full frame equivalent focal length is achieved through its 25x optical zoom. In the gallery above, the first shot was taken at 600mm while the second at 24mm, and both images pack a lot of detail.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/koPJ2V2hcfjuC2mb4eMafa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken with 25x optical zoom + 4x digital zoom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/H2oqnmXrnH5vbjMMnuCJ7b-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QqfrYzXTy2HhQnT7dKXsVc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 24mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>In addition to the optical zoom, you also get a 4x digital zoom. Once the camera has reached 600mm, it applies a 4x digital zoom and starts cropping to achieve 1200mm. Naturally, this detrimentally impacts image quality as you're cropping into an already modest 20.1MP and losing resolution. The first image of the cormorants above was taken with the 4x digital zoom, while the second was taken with the 25x optical zoom, and the third is the fully zoomed out original. As you can see in the first slide, there is a drop in resolution and detail. I’d recommend sticking with the 25x optical zoom to get the best images.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/hVaTwpPRC58sNv4UdUt5YY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Extreme crop of second image.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QNfbtSaVopXsfE34GFmtHZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Original image, taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Cropping after the fact in post-production yields similar results. The first shot in the gallery above is an extreme crop of the second photo, and once again, there's a drop in the quality. If you want a camera that allows for extreme cropping, you’ll have to splurge on the likes of the Sony A7 V or the Canon EOS R6 Mark III.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/9eVdVnGos5dnbD5nSWMSfZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mPgb9TWudejtMEjkwLVEZY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fTuAXvnhsCsAikTfzms9oa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/gFzAupsVop5uB2PeKAoQiZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/i94rhfK6zQpet95rQUSE3b-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/YfTRcRwp2Wa5p4EugGf3iZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/wGVUM6KS5dse9vjBuUKjXZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/my5mooKexmgcqDcW2hq4dZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/uNfNRb8dsqJgef9dkX8LyY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/pY2PMsAoGF7JgVGduFCpVZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/6VuMVLqxwaMUrPntUoeMPb-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/32Fyehx9h5z3vFTv6UKT8d-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Kacb443xQb6QGoHTxPHMvc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2Fwgdx5JmnHRsmxQV5QEWc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/vDh3WvT6h8qixbhFsLhQ6a-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Now that that’s out of the way, let’s finally talk about image quality — you can see sample shots in the gallery above. Color reproduction is stunning and accurate. The different shades and hues look beautiful, and I like that none of them look oversaturated or high on contrast. Even the neon-green algae looks vibrant but not overly bright.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/ufk6b4Eq5dSSbpfT35YNba-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zjgeDkaj2jD6ELkPwc8zra-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EwTnnbN5dUausmF9cgmbpZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/DoAfnyVgzFH6dJzpanRJZa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V also packs 12 of Sony’s Creative Looks, which are in-camera filters or presets, similar to those you’ll find on Canon or Fujifilm cameras. These include but aren’t limited to Portrait, Film, Soft Highkey, and others. The images of the swans above were shot using the Black & White Creative Look, and I think they look gorgeous. I love how dramatic it makes the swans look, and the camera has also done a good job of capturing the fine details, such as the water droplets on the first swan’s head, and the tiny feathers on the last bird’s beak.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/jJnCanpf3nutuMBAq5yufa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/28itQ3AW9sY4UBSSMa2NQa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>For the above photos of the kingfisher, I opted for the Vivid Creative Look which boosts contrast, saturation and vibrancy. The resulting images appear punchy, fitting for such a colorful bird. You can also customize each Creative Look’s parameters, like the contrast, highlights, and shadows, giving you more flexibility.</p><h2 id="high-speed-drive">High-speed drive</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/M2vnTC2oXbcHPPubN2bLhb-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/5VNJmWkpUvGhKtPZDcf3ib-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Lr6rWPSbxvBFAGm6HqoWZb-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>30fps blackout-free sequential shooting via its electronic shutter is one of the RX10 V’s headline specs, and it’s seriously impressive. It can capture 546 JPEGs, 141 RAW and JPEGs, or 162 lossless RAW files when using the 10fps mechanical shutter. Having 30fps drive at your disposal means you can capture every single moment of action, making the RX10 V a solid choice for wildlife and sports. In comparison, the Nikon Coolpix P1100 has a measly 7fps drive.</p><p>The electronic shutter has a speed of 30s to 1/16,000s, and I bumped the speed up to 1/8,000s to capture the sequence of the coot flying towards the other. The mechanical shutter, on the other hand, tops out at 1/2,000s, so I’d recommend using the electronic shutter when you can to get blur-free images.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Qsd46T7eYFxnK2rYRGYKZZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/eu6m2YBPRWp52KV34SUqZZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/YpQ2Mfqh9rbdpzBaTsKJhZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nBTC6uqXS53oG7B4dyLUbZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2NcCD9RJeit4JjLJMr5oZZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ynqbZjmrxAWDdqb3guEQcZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/PsKoCGXt5o9CgddYoD3yfZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/hxsdU6ZqVBAkowhXbo6qhZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2Q3KCzpCH2f3kpjSr27MfZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/jKtASKCJRdeKGdzrVszSgZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mt37knVkRTGxGrtjDt2vdZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Kd4Kbz8xTX6kqVViYuk2dZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Thanks to 30fps blackout-free burst and incredible autofocus that didn’t lose track of the tiny bird even once, I was able to capture the images (above) of the kingfisher taking off. The only downside to the RX10 V’s high-speed drive is that the camera doesn’t feature pre-burst capture, so it doesn’t start recording the frames when the shutter button is half-pressed. Again, you’ll need to get the Sony A7 V, the Canon EOS R6 Mark III or the OM System OM-1 Mark II if you want that.</p><h2 id="iso-performance">ISO performance</h2><p>The RX10 V doesn’t have as massive an ISO range as the one offered by the Sony A7 V — but it’s double the Nikon Coolpix P1100’s. The RX10 V has a base ISO of 64 and it tops out at 12,800. Given the camera’s small 1-inch sensor, high ISO performance isn’t anything to write home about.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/GmLNrK7Ta3aUWZGz8SYoLZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO8,000. JPEG export of ARW file.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zErphYTPJERbUj8gu8UCWY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO8,000. SOOC JPEG.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The results were as expected at ISO6,400, so I bumped the ISO up to 8,000. The first image in the gallery above is a JPEG export of the ARW file, while the second is the straight-out-of-camera (SOOC) JPEG. While the in-camera noise reduction algorithms have done well to reduce the grain in the SOOC JPEG, the image has lost some sharpness around the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/8bitdo-retro-cube-2-review">8BitDo Retro Cube 2</a>’s corners and edges, and the blue photobook looks too soft. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/6G2rgFCEavpmQUytTyEBWZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO12,800. JPEG export of ARW file.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ktPix7GYjNzQrTnQJVwKiY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO12,800. SOOC JPEG.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I then shot at ISO12,800. The first image (JPEG export of ARW file) has been ruined by noise, and the second shot (SOOC JPEG) isn’t that much better. You can see a lot of grain in the photobook as well as the speaker. The detail in the speaker’s grille has been lost, and the image appears flat. I wouldn’t recommend shooting at such high ISO unless and until it’s your last resort.</p><p>This is part and parcel of having a small sensor, but the RX10 V still performs better than the Nikon Coolpix P1100, whose 1/2.3-inch sensor produces extremely noisy images at just ISO6,400.</p><h2 id="dynamic-range">Dynamic range</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/QWHE5jxbL5cCqTTGCcJP9a-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO level 6, unedited.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/DqMaRZzFKdCuR9wyYyfwDa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO level 6, shadows boosted and highlights reduced in Adobe Photoshop Lightroom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V offers up to 9+ stops of dynamic range, and it features a D-Range Optimizer (DRO) mode which offers eight varying levels of balancing bright highlights and dark shadows.</p><p>The two images in the gallery above are the same shot, taken at DRO-6. The first image is the unedited RAW file. I boosted the shadows and reduced the highlights, which resulted in the second shot. Boosting the shadows has revealed a surprising amount of detail in the dark areas, such as the bushes in the distance. The shadows are fairly noise-free too, which is impressive.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/hxDyJjMNf3DUKCwNxSPvgb-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO disabled, unedited.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZsssQKvQzWhFs8ngoa5WyY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO disabled, shadows boosted and highlights reduced in Adobe Photoshop Lightroom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The backlit photo of the kingfisher above, on the other hand, was shot with DRO disabled. I edited the shot in Adobe Photoshop Lightroom and as you can see in the second slide, the camera has done a decent job of maintaining highlight detail while exposing for the bird’s comparatively dim unlit body. There’s some noticeable noise in the image, though, especially in the background (have a look at the bottom-left corner).</p><h2 class="article-body__section" id="section-sony-rx10-v-review-video"><span>Sony RX10 V review: Video</span></h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/FIVteoDdo2k" allowfullscreen></iframe></div></div><p>The Sony RX10 V is a competent hybrid camera, capable of recording 4K or FHD video at 60fps, and slow-mo 4K video at 120fps — the Nikon Coolpix P1100 can shoot 4K video at 30fps only. You can see a compilation of clips filmed at 4K/60fps above. I think the video looks great. Movement is fluid and smooth, while all the subjects remain in focus. The clips are fairly stable and not <em>too </em>shaky given that I was shooting handheld.</p><p>Similar to Sony’s vlogging cameras, you also get S-Log3 which captures a wide dynamic range, making it easier to color grade in post-production. If you want straight-out-of-camera slow-mo video, the camera’s slow-and-quick (S&Q) mode captures already slowed video (without any audio) which can be shared instantly without any need for editing in post.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-battery-life"><span>Sony RX10 V review: Battery life</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:5775px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="oUEaneDtJ5gCu34n37Vjga" name="Sony_RX10V-4" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/oUEaneDtJ5gCu34n37Vjga-1920-80.jpg" mos="" align="middle" fullscreen="" width="5775" height="3248" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>Unlike the NP-FW50 battery pack used by the Sony RX10 IV, the Sony RX10 V utilizes the NP-FZ100 battery pack. It’s the same battery used by the likes of the Sony A1 II and the Sony A7 V. Thanks to the NP-FZ100 battery, the RX10 V now lasts longer than the RX10 IV. It’s CIPA-rated for 630 shots (versus 400) when using the monitor or 570 shots (versus 370) with the EVF.</p><p>CIPA testing is conducted under strict conditions and the ratings are usually conservative. More often than not, your camera will last you longer in the field. In my testing, I was able to get over 3,000 shots, which included a mix of burst sequences and RAW images, and I walked away with just under 50% battery. Take this with a pinch of salt, though, as your mileage will vary depending on your settings and shooting conditions.</p><p>The RX10 V lasts much longer than the Nikon Coolpix P1100 which is CIPA-rated for just 260 shots, and the Panasonic Lumix DMC-FZ2000/FZ2500 which yields 270 shots on a single charge. The RX10 V also upstages mirrorless beasts, like the Sony A1 II (520 shots) and the Canon EOS R6 Mark III (620 shots).</p><h2 class="article-body__section" id="section-sony-rx10-v-review-verdict"><span>Sony RX10 V 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="cJ9soApjYBhfmusZYHAHzc" name="Sony_RX10_.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/cJ9soApjYBhfmusZYHAHzc-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you want a powerful bridge camera that feels like a mirrorless camera, I can’t recommend the Sony RX10 V enough. It packs a long-reaching lens with a 25x optical zoom, letting you get close-up shots of far-away birds. The RX10 V feels sublime to use and handle, thanks to improved ergonomics and controls. It’s got long battery life to boot, too.</p><p>What makes the RX10 V worth the premium is its fantastic and accurate autofocus, as well as 30fps blackout-free high-speed drive. Combined, both of these features make the RX10 V a formidable camera for sports and wildlife photography — or videography, as the camera is capable of recording 4K/60fps video.</p><p>While the camera doesn’t feature the best high ISO performance, given its small 1-inch sensor that can’t take on full frame beasts, this small complaint is overshadowed by everything else on offer. The RX10 V blurs the line between bridge and Sony’s Alpha lineup to deliver one of the best cameras of 2026.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/sony-rx10-v-review</link>
                                                                            <description>
                            <![CDATA[ The Sony RX10 V improves on its predecessor with better ergonomics, controls, longer battery life, 30fps blackout-free shooting, and incredible autofocus. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">56dffiHvhBWcRuCY7zNLWb</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/jPYBRm66nFP79ghiRy2vHa-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 27 Aug 2026 13:11:26 +0000</pubDate>                                                                                                                                <updated>Thu, 27 Aug 2026 22:47:34 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras & Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3-320-70.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres.&lt;/p&gt;&lt;p&gt;A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&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/jPYBRm66nFP79ghiRy2vHa-1920-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A black Sony RX10 V bridge camera]]></media:description>                                                            <media:text><![CDATA[A black Sony RX10 V bridge camera]]></media:text>
                                <media:title type="plain"><![CDATA[A black Sony RX10 V bridge camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/jPYBRm66nFP79ghiRy2vHa-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The king of bridge cameras returns. Nearly a decade since the fourth-gen camera came out, Sony has introduced substantial improvements and upgrades to make the RX10 V a simply outstanding camera. With better ergonomics and controls, and bigger battery life, the RX10 V feels more like a mirrorless Alpha camera — and I’m not complaining.</p><p>Handling the RX10 V is a joy, and its long-reaching 24-600mm (full frame equivalent) lens lets you get up, close and personal with your subjects. Thanks to 30fps blackout-free drive and excellent autofocus, this is a solid camera for amateur wildlife and sports photographers. The 20.1MP images boast beautiful color reproduction, too.</p><p>Though not without its flaws, there aren’t many reasons to not buy this camera. To find out if this is the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best camera </a>for you, read my full Sony RX10 V review.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-specs"><span>Sony RX10 V review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.adorama.com/sony-dsc-rx10-v-digital-camera/p/isorx10m5" target="_blank" rel="nofollow">$2,298</a> / <a href="https://www.lcegroup.co.uk/New/Sony-RX10-V-Digital-Camera_234898.html" target="_blank" rel="nofollow">£2,199</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>20.1MP 1-inch Stacked Exmor RS CMOS</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>BIONZ XR</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Optical SteadyShot</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Fast Hybrid AF (phase-detection AF + contrast-detection AF)</p></td></tr><tr><td class="firstcol " ><p><strong>Focus modes</strong></p></td><td  ><p>Single Shot, Continuous AF, DMF, Manual focus</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>0.5-inch Quad-VGA OLED EVF, approx. 3.68 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>3-inch TFT LCD touchscreen, approx. 1.6 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>ISO range</strong></p></td><td  ><p>64-12,800</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>4K/60fps | 4K/120fps | FHD/60fps</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>USB-C, micro HDMI, USB Micro-B 2.0, 3.5mm mic in, 3.5mm headphones out, 1x SD/SDHC/SDXC UHS-I/II</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless/bluetooth</strong></p></td><td  ><p>Wi-Fi, Bluetooth</p></td></tr><tr><td class="firstcol " ><p><strong>Max shooting speed</strong></p></td><td  ><p>30fps (electronic) | 10fps (mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Max shutter speed</strong></p></td><td  ><p>30s to 1/16,000s (stills, electronic) | 30s to 1/2,000s (stills, mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life (Stills, CIPA)</strong></p></td><td  ><p>630 shots (LCD) | 570 shots (EVF)</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>5.37 x 3.72 x 5.96 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.7lbs (with battery and card)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sony-rx10-v-review-price-availability"><span>Sony RX10 V 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="D7njNQMomC9joTYj4eczBd" name="Sony_RX10_ 1.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/D7njNQMomC9joTYj4eczBd-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V has been nearly a decade in the making, and it succeeds the RX10 IV which first came out in 2017. The RX10 V is a bridge camera that literally bridges the gap between basic point-and-shoot compacts and full frame cameras, and it borrows features from Sony’s Alpha lineup (more on that shortly). It retails for <a href="https://www.adorama.com/sony-dsc-rx10-v-digital-camera/p/isorx10m5" target="_blank" rel="nofollow">$2,298 at Adorama U.S.</a> / <a href="https://www.lcegroup.co.uk/New/Sony-RX10-V-Digital-Camera_234898.html" target="_blank" rel="nofollow">£2,199 at London Camera Exchange U.K.</a></p><p>It’s pricier than the <a href="https://www.tomsguide.com/cameras-photography/nikon-coolpix-p1100-review">Nikon Coolpix P1100</a> ($1,146) and the Panasonic Lumix DMC-FZ2000/FZ2500 ($1,049) but it’s worth the extra spend if you want 30fps blackout-free burst shooting, fantastic autofocus, 4K/60fps video, and long battery life.</p><p>Spending nearly $3,000 on a camera may sound like a lot. But given that the camera features a built-in 24-600mm (full frame equivalent) lens, it negates the need to drop nearly a grand or more on a lens. This softens the blows a bit — especially because a telephoto lens on its own can set you back serious money. For context, a Sony 400-800mm lens costs over $3,000, while Canon’s RF1200mm lens costs well over $22,000.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-design"><span>Sony RX10 V 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="D5jR7azS4uEABT7YnzZQ9d" name="Sony_RX10_ 3.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/D5jR7azS4uEABT7YnzZQ9d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Bridge cameras offer the convenience of point-and-shoot compacts and stuff it into DSLR-esque bodies. It’s no surprise, then, that the Sony RX10 V is slightly bulky, measuring 5.37 x 3.72 x 5.96 inches and weighing 2.7lbs (with battery and cards). It’s certainly bigger than my trusty mirrorless <a href="https://www.tomsguide.com/cameras-photography/fujifilm-x-t50-review">Fujifilm X-T50</a> ($1,399) but even so, it’s more compact and lighter than the Nikon Coolpix P1100.</p><p>Unlike the Nikon Coolpix P1100, the RX10 V doesn’t feel as unwieldy to handle, thanks to its spacious protruding grip, which is deep and provides plenty of space for your fingers to wrap around it. Compared to the RX10 IV, the fifth-gen model offers better ergonomics, from its deeper grip to its more prominent command dials. The RX10 V feels like a modern Alpha camera, like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-spent-a-day-shooting-on-the-new-sony-a7-v-here-are-my-first-impressions-spoiler-alert-its-great">Sony A7 V</a> ($2,899). Build quality feels solid, too, and I’d expect nothing less from Sony. </p><h2 id="viewfinder">Viewfinder</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:879px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="Tk6ZE7ercUexT3wNHJ2X7d" name="Sony_RX10_ 7.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/v2/w:1920,t:43,l:716,cw:879,ch:494,q:80/Tk6ZE7ercUexT3wNHJ2X7d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The RX10 V’s viewfinder has been upgraded. While its predecessor featured a 0.39-inch XGA OLED electronic viewfinder (EVF) with 0.7x magnification, the RX10 V packs a 0.5-inch Quad-VGA OLED EVF with 0.78x magnification — similar to the one on the flagship <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">Sony A1 II </a>($6,499).</p><p>The resolution has received a bump, too, from 2.35 million dots on the RX10 IV to 3.68 million dots on the RX10 V. You now get a sharp, detailed, life-like representation of the scene you’re capturing. This helps you accurately frame and capture your subject. The RX10 V’s EVF resolution is similar to the Sony A7 V’s (3.69M dots), the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review">Canon EOS R6 Mark III</a>’s (3.69M dots, $2,799) and the <a href="https://www.tomsguide.com/reviews/panasonic-lumix-g9-ii">Panasonic Lumix G9II</a>’s (3.68M dots, $1,899), though it’s slightly less than the Nikon Coolpix P1100’s (2.36M dots).</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:7365px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kJeKaFvJfxFCK8di2AFVra" name="Sony_RX10V" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/kJeKaFvJfxFCK8di2AFVra-1920-80.jpg" mos="" align="middle" fullscreen="" width="7365" height="4143" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>I’ve found the RX10 V’s viewfinder extremely comfortable to look through, even through my prescription glasses. I also love that its refresh rate can be set to either 60Hz or 120Hz — the same as on the Canon EOS R6 Mark III and the much pricier <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r5-mark-ii-review">Canon EOS R5 Mark II</a> ($4,299). 120Hz refresh rate ensures that the subjects you’re tracking remain free of motion blur, and the RX10 V delivers smooth and realistic motion, making it great for fast-paced sports and wildlife photography.</p><p>The RX10 V borrows another feature from the Sony A7 V, and that’s 30fps blackout-free burst shooting. This means that there are no black screens between shots, enabling you to keep a constant view of your subject as you shoot. This is something we’ve also seen on other wildlife-oriented cameras, like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-review">OM System OM-1 Mark II</a> ($2,399), and the RX10 V wins brownie points over the Canon EOS R6 Mark III here which doesn’t feature blackout-free sequential shooting.</p><h2 id="monitor">Monitor</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="Tk6ZE7ercUexT3wNHJ2X7d" name="Sony_RX10_ 7.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/Tk6ZE7ercUexT3wNHJ2X7d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The RX10 V’s rear monitor has received a modest upgrade, too. While the screen size remains the same — 3-inch on both the RX10 IV and the RX10 V — the touchscreen now has a resolution of 1.6 million dots. The 3:2 aspect ratio also matches the camera’s sensor now, as opposed to the 4:3 aspect ratio on the RX10 V’s predecessor.</p><p>I’ve found the RX10 V’s monitor highly responsive in my testing, and I’ve had no trouble viewing it in different lighting conditions, including direct sunlight. It’s also much brighter than the Nikon Coolpix P1100’s 921K-dot monitor. The RX10 V’s screen can tilt up and down but it can’t hinge or tilt sideways, like the Nikon Coolpix P1100’s can, though, which limits the extent of shooting at awkward, low-to-the-ground or overhead angles.</p><h2 id="ports">Ports</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="abHg4KmiWHT4D5i2uHMp6d" name="Sony_RX10_ 4.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/abHg4KmiWHT4D5i2uHMp6d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>All the ports are located on the RX10 V’s left edge and are protected by plastic flaps. You get a USB-C port for charging and data transfer; a legacy USB Micro-B port for accessories; a micro HDMI port; and dual 3.5mm ports for mic in and headphones out. The memory card slot is located next to the battery pack on the bottom plate.The RX10 V is compatible with a single SD/SDHC/SDXC UHS-I/II card.</p><h2 id="weather-sealing">Weather-sealing</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="7ytCwpXxA3Gc7jmJpscjCd" name="Sony_RX10_ 5.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/7ytCwpXxA3Gc7jmJpscjCd-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Unlike the Nikon Coolpix P1100, the RX10 V is fully weather-sealed and protected against moisture and dust. This gives you more confidence when shooting outdoors and in changeable weather conditions.</p><p>The RX10 V’s weather-sealing comes at a cost, though — but not a huge one. There’s no built-in flash, like on previous iterations of the line, but a small pop-up flash is rarely powerful enough to light up distant subjects (perhaps macro subjects, yes), so it’s not something you’ll necessarily miss.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-controls"><span>Sony RX10 V review: Controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9RS5FmpcSe7aHkAiAmGy4d" name="Sony_RX10_ 11.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/9RS5FmpcSe7aHkAiAmGy4d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V is a delight to use, thanks to its well-placed and large buttons and dials. On the RX10 IV, an LCD screen took up a fair bit of space on the top plate, but this has been replaced by a prominent command dial that lets you swap between shooting modes as well as stills, video, and slow-and-quick (S&Q) video. You now get additional dials for shutter speed and ISO, too.</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="wFs93caYnHmcFtxuY3Ud9d" name="Sony_RX10_ 13.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/wFs93caYnHmcFtxuY3Ud9d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I like that the dual custom (C1 and C2) buttons have been moved up and are now located next to the shutter button. This makes accessing them much easier, and it makes shooting feel more intuitive. Sony has also added a physical focus joystick, similar to the one on its latest Alpha models, on the rear panel — something that was absent on the RX10 IV. The navigation wheel is bigger, too, and again, feels like it’s been borrowed from the A7 V or the A1 II.</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="5C4qbvPkai7F8T7xRJzg6d" name="Sony_RX10_ 10.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/5C4qbvPkai7F8T7xRJzg6d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>These quality of life improvements result not only in better ergonomics, but also in a more well-rounded user experience. My only complaint is that the RX10 V’s three dials are only accessible by your right thumb, and I would have much preferred having a front dial for adjusting, say, shutter speed. Maybe sixth time’s the charm, eh, Sony?</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="35J5e8aib7AhbiUV38Yy6d" name="Sony_RX10_ 9.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/35J5e8aib7AhbiUV38Yy6d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>A decade is a long time, and it isn’t surprising that the RX10 IV’s menu system has been overhauled to deliver a more user-friendly experience on the RX 10 V. Instead of being text-heavy, the RX10 V’s menu system is color-coded, feels more logical and less cluttered, and it’s clearly been modeled after recent Alpha cameras.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-autofocus"><span>Sony RX10 V review: Autofocus</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="nkcaZDv8EKT9uRjAoukS8d" name="Sony_RX10_ 6.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/nkcaZDv8EKT9uRjAoukS8d-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V features an advanced hybrid autofocus system which uses a combination of phase- and contrast-detection AF, driven by the BIONZ XR processor. It inherits the Sony A7 V’s AI chip with real-time subject detection autofocus, and the Sony A1 II’s Auto Subject Detection which automatically swaps between human, bird/animal, insect, and vehicle detection modes.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/JFE9mn8hPpRNXpxfzF8wca-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/iRW75EF84nSRu3G2P2wW4d-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fVjajBpMmn5KoRLSdfW2mc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/8fmPW9UNj8EPUqrfaZYmBd-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/bFDXmBF9nrRLzKcQj3rTKa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/4CpeZt9uE28bQJxuwVeoJb-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Regardless of the subject I was photographing, the RX10 V didn’t let me down. As you can see in the gallery above, the camera was quick to find and lock on to birds, humans, and insects. When I was photographing birds and insects at a lake, I appreciated the camera’s Auto Subject Detection, and I found it very reliable. I was able to get the shots I needed as I didn’t have to waste time swapping between subjects manually.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/LXnxDGR9jCmMCNgcBzuaTc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/rr3xKP6dgojmVm3NVCwnaZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The photos above aren’t my finest work, but I wanted to use these two photos to demonstrate how well the RX10 V’s autofocus works. Both the squirrel and bird were obscured by leaves, and because of very bright sunlight, I couldn’t focus on them with my own eyes — so I pointed my camera right overhead and the RX10 V immediately locked on to them. <em>That’s </em>how smart the camera’s AF system is.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/4ptzqxEicPqDN7Fx5Tgvec-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/uAEw5jkaU6BDFKBxondJAd-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/euKhgZ5EbrzNd6JcUwPLcc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9bZFMauzQ4t7LDrmLo8LYc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I also appreciated the RX10 V locking on to and tracking the black-headed gull as I fired off a burst. The autofocus didn’t break at all, even as the gull landed on the water, surrounded by other birds. As you can see in the gallery above, it’s still in focus in the last two shots. </p><h2 class="article-body__section" id="section-sony-rx10-v-review-stabilization"><span>Sony RX10 V review: Stabilization</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/iR74apVdMuhtB9pboSqCyX-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 1/3s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MLd3MR7SrVTM6ktQgxZwUX-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 1/2s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V utilizes Sony’s SteadyShot optical image stabilization which is also found in the <a href="https://www.tomsguide.com/cameras-photography/sony-zv-e10-ii-review">Sony ZV-E10 II</a> ($999) and the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-rx1r-iii-review">Sony RX1R III</a> ($5,099), and it incurs a crop. The lack of in-body image stabilization (IBIS) means you can’t shoot at super slow shutter speeds, but it should just about suffice when shooting handheld.</p><p>In my testing, I was able to shoot handheld at shutter speeds as slow as 1/3s (first image above) but any slower than that, like 1/2s (second image above), and the resulting image was blurry, impacted by camera shake. This is still good, though!</p><h2 class="article-body__section" id="section-sony-rx10-v-review-image-quality"><span>Sony RX10 V review: Image quality</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:4867px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="9g4U23oDsUQ9m8i9Fn36ca" name="Sony_RX10V-2" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/9g4U23oDsUQ9m8i9Fn36ca-1920-80.jpg" mos="" align="middle" fullscreen="" width="4867" height="2738" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V packs a small 1-inch Stacked Exmor RS CMOS sensor with a 20.1MP resolution. Though small, its sensor is still bigger than the Nikon Coolpix P1100’s 1/2.3-inch 16MP one. You can’t make extreme crops as you can on, say, the 33MP full frame Sony A7 V, but given that you get a 24-600mm effective focal length, you can still capture detailed images of distant subjects.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/aKy3htXcWfzsPYo6ayoiic-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/NjLg2nEStFVDRLaPk4cVoc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 24mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Before we dive into image quality, let’s talk about the RX10 V’s reach. The RX10 V is fitted with a ZEISS Vario-Sonnar T 24-600mm lens, and this full frame equivalent focal length is achieved through its 25x optical zoom. In the gallery above, the first shot was taken at 600mm while the second at 24mm, and both images pack a lot of detail.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/koPJ2V2hcfjuC2mb4eMafa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken with 25x optical zoom + 4x digital zoom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/H2oqnmXrnH5vbjMMnuCJ7b-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QqfrYzXTy2HhQnT7dKXsVc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 24mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>In addition to the optical zoom, you also get a 4x digital zoom. Once the camera has reached 600mm, it applies a 4x digital zoom and starts cropping to achieve 1200mm. Naturally, this detrimentally impacts image quality as you're cropping into an already modest 20.1MP and losing resolution. The first image of the cormorants above was taken with the 4x digital zoom, while the second was taken with the 25x optical zoom, and the third is the fully zoomed out original. As you can see in the first slide, there is a drop in resolution and detail. I’d recommend sticking with the 25x optical zoom to get the best images.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/hVaTwpPRC58sNv4UdUt5YY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Extreme crop of second image.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QNfbtSaVopXsfE34GFmtHZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Original image, taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Cropping after the fact in post-production yields similar results. The first shot in the gallery above is an extreme crop of the second photo, and once again, there's a drop in the quality. If you want a camera that allows for extreme cropping, you’ll have to splurge on the likes of the Sony A7 V or the Canon EOS R6 Mark III.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/9eVdVnGos5dnbD5nSWMSfZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mPgb9TWudejtMEjkwLVEZY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fTuAXvnhsCsAikTfzms9oa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/gFzAupsVop5uB2PeKAoQiZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/i94rhfK6zQpet95rQUSE3b-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/YfTRcRwp2Wa5p4EugGf3iZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/wGVUM6KS5dse9vjBuUKjXZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/my5mooKexmgcqDcW2hq4dZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/uNfNRb8dsqJgef9dkX8LyY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/pY2PMsAoGF7JgVGduFCpVZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/6VuMVLqxwaMUrPntUoeMPb-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/32Fyehx9h5z3vFTv6UKT8d-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Kacb443xQb6QGoHTxPHMvc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2Fwgdx5JmnHRsmxQV5QEWc-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/vDh3WvT6h8qixbhFsLhQ6a-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Now that that’s out of the way, let’s finally talk about image quality — you can see sample shots in the gallery above. Color reproduction is stunning and accurate. The different shades and hues look beautiful, and I like that none of them look oversaturated or high on contrast. Even the neon-green algae looks vibrant but not overly bright.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/ufk6b4Eq5dSSbpfT35YNba-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zjgeDkaj2jD6ELkPwc8zra-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EwTnnbN5dUausmF9cgmbpZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/DoAfnyVgzFH6dJzpanRJZa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V also packs 12 of Sony’s Creative Looks, which are in-camera filters or presets, similar to those you’ll find on Canon or Fujifilm cameras. These include but aren’t limited to Portrait, Film, Soft Highkey, and others. The images of the swans above were shot using the Black & White Creative Look, and I think they look gorgeous. I love how dramatic it makes the swans look, and the camera has also done a good job of capturing the fine details, such as the water droplets on the first swan’s head, and the tiny feathers on the last bird’s beak.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/jJnCanpf3nutuMBAq5yufa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/28itQ3AW9sY4UBSSMa2NQa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>For the above photos of the kingfisher, I opted for the Vivid Creative Look which boosts contrast, saturation and vibrancy. The resulting images appear punchy, fitting for such a colorful bird. You can also customize each Creative Look’s parameters, like the contrast, highlights, and shadows, giving you more flexibility.</p><h2 id="high-speed-drive">High-speed drive</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/M2vnTC2oXbcHPPubN2bLhb-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/5VNJmWkpUvGhKtPZDcf3ib-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Lr6rWPSbxvBFAGm6HqoWZb-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>30fps blackout-free sequential shooting via its electronic shutter is one of the RX10 V’s headline specs, and it’s seriously impressive. It can capture 546 JPEGs, 141 RAW and JPEGs, or 162 lossless RAW files when using the 10fps mechanical shutter. Having 30fps drive at your disposal means you can capture every single moment of action, making the RX10 V a solid choice for wildlife and sports. In comparison, the Nikon Coolpix P1100 has a measly 7fps drive.</p><p>The electronic shutter has a speed of 30s to 1/16,000s, and I bumped the speed up to 1/8,000s to capture the sequence of the coot flying towards the other. The mechanical shutter, on the other hand, tops out at 1/2,000s, so I’d recommend using the electronic shutter when you can to get blur-free images.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Qsd46T7eYFxnK2rYRGYKZZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/eu6m2YBPRWp52KV34SUqZZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/YpQ2Mfqh9rbdpzBaTsKJhZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nBTC6uqXS53oG7B4dyLUbZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2NcCD9RJeit4JjLJMr5oZZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ynqbZjmrxAWDdqb3guEQcZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/PsKoCGXt5o9CgddYoD3yfZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/hxsdU6ZqVBAkowhXbo6qhZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2Q3KCzpCH2f3kpjSr27MfZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/jKtASKCJRdeKGdzrVszSgZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mt37knVkRTGxGrtjDt2vdZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Kd4Kbz8xTX6kqVViYuk2dZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Thanks to 30fps blackout-free burst and incredible autofocus that didn’t lose track of the tiny bird even once, I was able to capture the images (above) of the kingfisher taking off. The only downside to the RX10 V’s high-speed drive is that the camera doesn’t feature pre-burst capture, so it doesn’t start recording the frames when the shutter button is half-pressed. Again, you’ll need to get the Sony A7 V, the Canon EOS R6 Mark III or the OM System OM-1 Mark II if you want that.</p><h2 id="iso-performance">ISO performance</h2><p>The RX10 V doesn’t have as massive an ISO range as the one offered by the Sony A7 V — but it’s double the Nikon Coolpix P1100’s. The RX10 V has a base ISO of 64 and it tops out at 12,800. Given the camera’s small 1-inch sensor, high ISO performance isn’t anything to write home about.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/GmLNrK7Ta3aUWZGz8SYoLZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO8,000. JPEG export of ARW file.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zErphYTPJERbUj8gu8UCWY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO8,000. SOOC JPEG.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The results were as expected at ISO6,400, so I bumped the ISO up to 8,000. The first image in the gallery above is a JPEG export of the ARW file, while the second is the straight-out-of-camera (SOOC) JPEG. While the in-camera noise reduction algorithms have done well to reduce the grain in the SOOC JPEG, the image has lost some sharpness around the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/8bitdo-retro-cube-2-review">8BitDo Retro Cube 2</a>’s corners and edges, and the blue photobook looks too soft. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/6G2rgFCEavpmQUytTyEBWZ-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO12,800. JPEG export of ARW file.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ktPix7GYjNzQrTnQJVwKiY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO12,800. SOOC JPEG.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I then shot at ISO12,800. The first image (JPEG export of ARW file) has been ruined by noise, and the second shot (SOOC JPEG) isn’t that much better. You can see a lot of grain in the photobook as well as the speaker. The detail in the speaker’s grille has been lost, and the image appears flat. I wouldn’t recommend shooting at such high ISO unless and until it’s your last resort.</p><p>This is part and parcel of having a small sensor, but the RX10 V still performs better than the Nikon Coolpix P1100, whose 1/2.3-inch sensor produces extremely noisy images at just ISO6,400.</p><h2 id="dynamic-range">Dynamic range</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/QWHE5jxbL5cCqTTGCcJP9a-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO level 6, unedited.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/DqMaRZzFKdCuR9wyYyfwDa-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO level 6, shadows boosted and highlights reduced in Adobe Photoshop Lightroom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V offers up to 9+ stops of dynamic range, and it features a D-Range Optimizer (DRO) mode which offers eight varying levels of balancing bright highlights and dark shadows.</p><p>The two images in the gallery above are the same shot, taken at DRO-6. The first image is the unedited RAW file. I boosted the shadows and reduced the highlights, which resulted in the second shot. Boosting the shadows has revealed a surprising amount of detail in the dark areas, such as the bushes in the distance. The shadows are fairly noise-free too, which is impressive.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/hxDyJjMNf3DUKCwNxSPvgb-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO disabled, unedited.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZsssQKvQzWhFs8ngoa5WyY-1920-80.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO disabled, shadows boosted and highlights reduced in Adobe Photoshop Lightroom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The backlit photo of the kingfisher above, on the other hand, was shot with DRO disabled. I edited the shot in Adobe Photoshop Lightroom and as you can see in the second slide, the camera has done a decent job of maintaining highlight detail while exposing for the bird’s comparatively dim unlit body. There’s some noticeable noise in the image, though, especially in the background (have a look at the bottom-left corner).</p><h2 class="article-body__section" id="section-sony-rx10-v-review-video"><span>Sony RX10 V review: Video</span></h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/FIVteoDdo2k" allowfullscreen></iframe></div></div><p>The Sony RX10 V is a competent hybrid camera, capable of recording 4K or FHD video at 60fps, and slow-mo 4K video at 120fps — the Nikon Coolpix P1100 can shoot 4K video at 30fps only. You can see a compilation of clips filmed at 4K/60fps above. I think the video looks great. Movement is fluid and smooth, while all the subjects remain in focus. The clips are fairly stable and not <em>too </em>shaky given that I was shooting handheld.</p><p>Similar to Sony’s vlogging cameras, you also get S-Log3 which captures a wide dynamic range, making it easier to color grade in post-production. If you want straight-out-of-camera slow-mo video, the camera’s slow-and-quick (S&Q) mode captures already slowed video (without any audio) which can be shared instantly without any need for editing in post.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-battery-life"><span>Sony RX10 V review: Battery life</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:5775px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="oUEaneDtJ5gCu34n37Vjga" name="Sony_RX10V-4" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/oUEaneDtJ5gCu34n37Vjga-1920-80.jpg" mos="" align="middle" fullscreen="" width="5775" height="3248" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>Unlike the NP-FW50 battery pack used by the Sony RX10 IV, the Sony RX10 V utilizes the NP-FZ100 battery pack. It’s the same battery used by the likes of the Sony A1 II and the Sony A7 V. Thanks to the NP-FZ100 battery, the RX10 V now lasts longer than the RX10 IV. It’s CIPA-rated for 630 shots (versus 400) when using the monitor or 570 shots (versus 370) with the EVF.</p><p>CIPA testing is conducted under strict conditions and the ratings are usually conservative. More often than not, your camera will last you longer in the field. In my testing, I was able to get over 3,000 shots, which included a mix of burst sequences and RAW images, and I walked away with just under 50% battery. Take this with a pinch of salt, though, as your mileage will vary depending on your settings and shooting conditions.</p><p>The RX10 V lasts much longer than the Nikon Coolpix P1100 which is CIPA-rated for just 260 shots, and the Panasonic Lumix DMC-FZ2000/FZ2500 which yields 270 shots on a single charge. The RX10 V also upstages mirrorless beasts, like the Sony A1 II (520 shots) and the Canon EOS R6 Mark III (620 shots).</p><h2 class="article-body__section" id="section-sony-rx10-v-review-verdict"><span>Sony RX10 V 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="cJ9soApjYBhfmusZYHAHzc" name="Sony_RX10_.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/cJ9soApjYBhfmusZYHAHzc-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you want a powerful bridge camera that feels like a mirrorless camera, I can’t recommend the Sony RX10 V enough. It packs a long-reaching lens with a 25x optical zoom, letting you get close-up shots of far-away birds. The RX10 V feels sublime to use and handle, thanks to improved ergonomics and controls. It’s got long battery life to boot, too.</p><p>What makes the RX10 V worth the premium is its fantastic and accurate autofocus, as well as 30fps blackout-free high-speed drive. Combined, both of these features make the RX10 V a formidable camera for sports and wildlife photography — or videography, as the camera is capable of recording 4K/60fps video.</p><p>While the camera doesn’t feature the best high ISO performance, given its small 1-inch sensor that can’t take on full frame beasts, this small complaint is overshadowed by everything else on offer. The RX10 V blurs the line between bridge and Sony’s Alpha lineup to deliver one of the best cameras of 2026.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Cheaper OLED TVs might finally be on the way — here's when they could arrive ]]></title>
                                                                                                <dc:content><![CDATA[ <p>OLED TVs have long since been the darling of A/V aficionados, home theater enthusiasts and TV dorks like myself. While OLEDs aren’t ideal for every viewing environment, the technology is nonetheless capable of performance- and design-related feats that even the top Mini-LED TVs can’t pull off.</p><p>There’s just one problem: OLED TVs don’t come cheap. Even entry-level OLEDs tend to be priced higher than mid-range Mini-LED TVs, and the <a href="https://www.tomsguide.com/tvs/oled-tvs/best-oled-tvs">best OLED TVs</a> are often more expensive than some brands’ best LCD-based sets.</p><p>But there are signs that the status quo could be changing. This year, LG Display is manufacturing cheaper OLED panels for LG and its competitors, and it’s happening at a time where the top OLED-maker is also reportedly developing an even-cheaper panel for the next generation of OLED TVs. Here’s how these advancements could usher in an era of cheap OLEDs.</p><h2 id="lg-display-s-quest-to-make-oled-cheaper-more-efficient-and-longer-lasting">LG Display’s quest to make OLED cheaper, more efficient and longer lasting</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="z8cwLfSQU23Gt58Y7C5R5n" name="LG-B6--11" alt="LG B6 OLED TV on a table" src="https://cdn.mos.cms.futurecdn.net/z8cwLfSQU23Gt58Y7C5R5n-1920-80.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>There are two exciting developments in the realm of affordable OLED technology playing out as we speak. The first spin on OLED technology is already available to buy and it's called OLED SE.</p><p>Announced back in March by <a href="https://news.lgdisplay.com/en/2026/03/from-premium-to-entry-levellg-display-expands-the-oled-tv-market-with-new-sepanels/?ckattempt=2" target="_blank" rel="nofollow">LG Display</a>, OLED SE sits below OLED EX, the latter of which is the panel-type associated with the mid-range <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled">LG C6</a>. OLED EX, however, sits below OLED META, the panel-type associated with the flagship-level <a href="https://www.tomsguide.com/tvs/oled-tvs/the-lg-g6-oled-has-a-new-feature-i-desperately-need-and-it-could-be-the-biggest-tv-upgrade-in-years">LG G6</a>. This makes the OLED SE one of the most affordable panel options for any brand looking to sell an OLED TV.</p><p>There’s been speculation among industry insiders that the all-new LG B6E OLED — one of LG’s most-affordable OLED TVs this year — is built around LG Display’s OLED SE panel. We’ve yet to take this model for a test spin in our lab, but there’s no denying its relatively friendly price tag of just $899 for a 55-inch model.</p><div class="product"><a data-dimension112="0b677852-a188-11f1-ae6b-2f2bff7b20f9" data-action="Deal Block" data-label="55" B6E 4K OLED TV" data-dimension48="55" B6E 4K OLED TV" data-dimension25="$899" href="https://www.bestbuy.com/product/lg-55-class-b6e-series-oled-ai-4k-smart-webos-tv-2026/JJ8VPZZ7PZ" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="2FCFUfHnMLGgUA6cgCxeVF" name="LG-B6E-OLED-TV-16-by-9-deal-block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/2FCFUfHnMLGgUA6cgCxeVF-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/lg-55-class-b6e-series-oled-ai-4k-smart-webos-tv-2026/JJ8VPZZ7PZ" target="_blank" rel="nofollow" data-dimension112="0b677852-a188-11f1-ae6b-2f2bff7b20f9" data-action="Deal Block" data-label='55" B6E 4K OLED TV' data-dimension48='55" B6E 4K OLED TV' data-dimension25="$899">55" B6E 4K OLED TV: was $1299 now $899</a></strong><br>The LG B6E is a fantastic, entry-level OLED TV for folks who are intrigued by this high-performance display tech, but who'd rather not shell out for a high-end panel. In addition to being one of the most-affordable OLED TVs on shelves in 2026, it also comes with webOS built in and a wide selection of gaming-related features.<a class="view-deal button" href="https://www.bestbuy.com/product/lg-55-class-b6e-series-oled-ai-4k-smart-webos-tv-2026/JJ8VPZZ7PZ" target="_blank" rel="nofollow" data-dimension112="0b677852-a188-11f1-ae6b-2f2bff7b20f9" data-action="Deal Block" data-label="55" B6E 4K OLED TV" data-dimension48="55" B6E 4K OLED TV" data-dimension25="$899">View Deal</a></p></div><p>Earlier this year, rumors began circulating that a <a href="https://www.tomsguide.com/tvs/oled-tvs/is-this-rumored-sony-oled-tv-the-lg-c6-rival-weve-been-waiting-for">new Sony OLED</a> could be on the horizon — a more-affordable set that could compete with entry-level and mid-range OLEDs from Samsung and Sony. At the time, I floated the possibility that this TV could very well leverage LG Display’s OLED SE panel, too.</p><p>Even if we assume that OLED SE panels are present in TVs like the LG B6E (and in entry-level OLEDs from LG’s competitors), $899 for an entry-level model isn’t exactly <em>cheap</em>. Fortunately, it doesn’t seem like LG Display’s work is done.</p><h2 id="what-is-two-stack-oled">What is two-stack OLED?</h2><p>According to Korean newspaper <a href="https://www.etnews.com/20260731000155">ETNews</a> (as reported by <a href="https://www.flatpanelshd.com/news.php?subaction=showfull&id=1785923042">FlatpanelsHD</a>), LG Display is reportedly developing an all-new, “two-stack” WOLED panel for use in TVs and monitors. The design is built around a simplified structure that relies on just two emission layers, while previous WOLED-based designs (like the one found on the LG C6) rely on three layers.</p><div><blockquote><p>The design is built around a simplified structure that relies on just two emission layers.</p></blockquote></div><p>LG Display claims that the new two-stack structure is capable of covering about 82.5% of BT.2020. </p><p>If true, that would be an impressive step up from what we've measured on the three-stack OLED panels we've tested, which tend to settle in the 75% to 77% range.</p><p>While there's no guarantee that future OLED TVs built around this new, two-stack structure will ultimately be cheaper than what we're used to seeing within the entry-level and mid-range class, it's likely a net-positive for the market (and shoppers) for the manufacturing of OLED technology to get more efficient in the long run.</p><h2 id="cheaper-oleds-in-2027-and-beyond">Cheaper OLEDs in 2027 and beyond</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.15%;"><img id="GWsne6iGdwXHY8nNvFcrMh" name="LG-C6--14" alt="The LG C6 OLED at Future's testing labs in New York." src="https://cdn.mos.cms.futurecdn.net/GWsne6iGdwXHY8nNvFcrMh-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The development of new OLED technology won't immediately crash the price of OLED TVs around the world. But new, more-cost-effective ways of manufacturing this infamously finnicky display tech is essential to its long-term viability.</p><p>Whether we're talking OLED SE, "two-stack" WOLED or something not yet fully realized, these developments are all stops on the road to OLED TVs' becoming less of a luxury for the average person.</p><p>Shared adoption of these new panel types from brands like Samsung, Sony and others is key, too, because the more options people have when buying a new TV, the better (and cheaper) those options are likely to become. Additionally, if LG Display is successful in these endeavors, it could entice TV brands that haven't previously developed OLED TVs to finally take the plunge.</p><div><blockquote><p>If LG Display is successful in these endeavors, it could entice TV brands that haven't previously developed OLED TVs to finally take the plunge.</p></blockquote></div><p>In 2027 and beyond, OLED TVs could see even cheaper pricing, provided these advancements aren't offset by additional supply-chain shortages. </p><p>It's possible that the next major OLED frontier <em>won't</em> be about making these TVs bigger and brighter, but rather, leaner and cheaper.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.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/tvs/4k-tvs/rgb-led-vs-tandem-oled-tvs-weve-tested-both-and-here-are-the-differences">RGB LED vs Tandem OLED TVs: We’ve tested both and here are the differences</a></li><li><a href="https://www.tomsguide.com/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up">We just graded every TV brand on price, performance and more — here’s how their report cards stack up</a></li><li><a href="https://www.tomsguide.com/tvs/tcl-and-sony-are-joining-forces-to-sell-more-tvs-but-is-that-bad-news-for-shoppers">TCL and Sony are joining forces to sell more TVs — but is that bad news for shoppers?</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/oled-tvs/cheaper-oled-tvs-might-finally-be-on-the-way-heres-when-they-could-arrive</link>
                                                                            <description>
                            <![CDATA[ LG Display has developed two new types of OLED panels aimed to cut manufacturing costs without sacrificing performance. Here's what to know about OLED SE and the new, "two-stack" OLED design. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">vi42A2p4wNFq2D87GzYKAC</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/VusmP5N5Xg6B4xnE38CZXh-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 27 Aug 2026 10:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[OLED TVs]]></category>
                                                    <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ michael.desjardin@futurenet.com (Michael Desjardin) ]]></author>                    <dc:creator><![CDATA[ Michael Desjardin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/esytxRqW9GNVZNVCf7BFyV-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael Desjardin is a Senior Editor for TVs at Tom&#039;s Guide. He&#039;s been testing and tinkering with TVs professionally for over a decade, previously for Reviewed and USA Today. Michael studied media production and screenwriting at Emerson College. He loves cooking, zoning out to ambient music, and getting way too invested in the Red Sox. He considers himself living proof that TV doesn&#039;t necessarily rot your brain.&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/VusmP5N5Xg6B4xnE38CZXh-1920-80.jpg">
                                                            <media:credit><![CDATA[LG]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The LG B4 OLED on a living room wall.]]></media:description>                                                            <media:text><![CDATA[The LG B4 OLED on a living room wall.]]></media:text>
                                <media:title type="plain"><![CDATA[The LG B4 OLED on a living room wall.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/VusmP5N5Xg6B4xnE38CZXh-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>OLED TVs have long since been the darling of A/V aficionados, home theater enthusiasts and TV dorks like myself. While OLEDs aren’t ideal for every viewing environment, the technology is nonetheless capable of performance- and design-related feats that even the top Mini-LED TVs can’t pull off.</p><p>There’s just one problem: OLED TVs don’t come cheap. Even entry-level OLEDs tend to be priced higher than mid-range Mini-LED TVs, and the <a href="https://www.tomsguide.com/tvs/oled-tvs/best-oled-tvs">best OLED TVs</a> are often more expensive than some brands’ best LCD-based sets.</p><p>But there are signs that the status quo could be changing. This year, LG Display is manufacturing cheaper OLED panels for LG and its competitors, and it’s happening at a time where the top OLED-maker is also reportedly developing an even-cheaper panel for the next generation of OLED TVs. Here’s how these advancements could usher in an era of cheap OLEDs.</p><h2 id="lg-display-s-quest-to-make-oled-cheaper-more-efficient-and-longer-lasting">LG Display’s quest to make OLED cheaper, more efficient and longer lasting</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="z8cwLfSQU23Gt58Y7C5R5n" name="LG-B6--11" alt="LG B6 OLED TV on a table" src="https://cdn.mos.cms.futurecdn.net/z8cwLfSQU23Gt58Y7C5R5n-1920-80.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>There are two exciting developments in the realm of affordable OLED technology playing out as we speak. The first spin on OLED technology is already available to buy and it's called OLED SE.</p><p>Announced back in March by <a href="https://news.lgdisplay.com/en/2026/03/from-premium-to-entry-levellg-display-expands-the-oled-tv-market-with-new-sepanels/?ckattempt=2" target="_blank" rel="nofollow">LG Display</a>, OLED SE sits below OLED EX, the latter of which is the panel-type associated with the mid-range <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled">LG C6</a>. OLED EX, however, sits below OLED META, the panel-type associated with the flagship-level <a href="https://www.tomsguide.com/tvs/oled-tvs/the-lg-g6-oled-has-a-new-feature-i-desperately-need-and-it-could-be-the-biggest-tv-upgrade-in-years">LG G6</a>. This makes the OLED SE one of the most affordable panel options for any brand looking to sell an OLED TV.</p><p>There’s been speculation among industry insiders that the all-new LG B6E OLED — one of LG’s most-affordable OLED TVs this year — is built around LG Display’s OLED SE panel. We’ve yet to take this model for a test spin in our lab, but there’s no denying its relatively friendly price tag of just $899 for a 55-inch model.</p><div class="product"><a data-dimension112="0b677852-a188-11f1-ae6b-2f2bff7b20f9" data-action="Deal Block" data-label="55" B6E 4K OLED TV" data-dimension48="55" B6E 4K OLED TV" data-dimension25="$899" href="https://www.bestbuy.com/product/lg-55-class-b6e-series-oled-ai-4k-smart-webos-tv-2026/JJ8VPZZ7PZ" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="2FCFUfHnMLGgUA6cgCxeVF" name="LG-B6E-OLED-TV-16-by-9-deal-block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/2FCFUfHnMLGgUA6cgCxeVF-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/lg-55-class-b6e-series-oled-ai-4k-smart-webos-tv-2026/JJ8VPZZ7PZ" target="_blank" rel="nofollow" data-dimension112="0b677852-a188-11f1-ae6b-2f2bff7b20f9" data-action="Deal Block" data-label='55" B6E 4K OLED TV' data-dimension48='55" B6E 4K OLED TV' data-dimension25="$899">55" B6E 4K OLED TV: was $1299 now $899</a></strong><br>The LG B6E is a fantastic, entry-level OLED TV for folks who are intrigued by this high-performance display tech, but who'd rather not shell out for a high-end panel. In addition to being one of the most-affordable OLED TVs on shelves in 2026, it also comes with webOS built in and a wide selection of gaming-related features.<a class="view-deal button" href="https://www.bestbuy.com/product/lg-55-class-b6e-series-oled-ai-4k-smart-webos-tv-2026/JJ8VPZZ7PZ" target="_blank" rel="nofollow" data-dimension112="0b677852-a188-11f1-ae6b-2f2bff7b20f9" data-action="Deal Block" data-label="55" B6E 4K OLED TV" data-dimension48="55" B6E 4K OLED TV" data-dimension25="$899">View Deal</a></p></div><p>Earlier this year, rumors began circulating that a <a href="https://www.tomsguide.com/tvs/oled-tvs/is-this-rumored-sony-oled-tv-the-lg-c6-rival-weve-been-waiting-for">new Sony OLED</a> could be on the horizon — a more-affordable set that could compete with entry-level and mid-range OLEDs from Samsung and Sony. At the time, I floated the possibility that this TV could very well leverage LG Display’s OLED SE panel, too.</p><p>Even if we assume that OLED SE panels are present in TVs like the LG B6E (and in entry-level OLEDs from LG’s competitors), $899 for an entry-level model isn’t exactly <em>cheap</em>. Fortunately, it doesn’t seem like LG Display’s work is done.</p><h2 id="what-is-two-stack-oled">What is two-stack OLED?</h2><p>According to Korean newspaper <a href="https://www.etnews.com/20260731000155">ETNews</a> (as reported by <a href="https://www.flatpanelshd.com/news.php?subaction=showfull&id=1785923042">FlatpanelsHD</a>), LG Display is reportedly developing an all-new, “two-stack” WOLED panel for use in TVs and monitors. The design is built around a simplified structure that relies on just two emission layers, while previous WOLED-based designs (like the one found on the LG C6) rely on three layers.</p><div><blockquote><p>The design is built around a simplified structure that relies on just two emission layers.</p></blockquote></div><p>LG Display claims that the new two-stack structure is capable of covering about 82.5% of BT.2020. </p><p>If true, that would be an impressive step up from what we've measured on the three-stack OLED panels we've tested, which tend to settle in the 75% to 77% range.</p><p>While there's no guarantee that future OLED TVs built around this new, two-stack structure will ultimately be cheaper than what we're used to seeing within the entry-level and mid-range class, it's likely a net-positive for the market (and shoppers) for the manufacturing of OLED technology to get more efficient in the long run.</p><h2 id="cheaper-oleds-in-2027-and-beyond">Cheaper OLEDs in 2027 and beyond</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.15%;"><img id="GWsne6iGdwXHY8nNvFcrMh" name="LG-C6--14" alt="The LG C6 OLED at Future's testing labs in New York." src="https://cdn.mos.cms.futurecdn.net/GWsne6iGdwXHY8nNvFcrMh-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The development of new OLED technology won't immediately crash the price of OLED TVs around the world. But new, more-cost-effective ways of manufacturing this infamously finnicky display tech is essential to its long-term viability.</p><p>Whether we're talking OLED SE, "two-stack" WOLED or something not yet fully realized, these developments are all stops on the road to OLED TVs' becoming less of a luxury for the average person.</p><p>Shared adoption of these new panel types from brands like Samsung, Sony and others is key, too, because the more options people have when buying a new TV, the better (and cheaper) those options are likely to become. Additionally, if LG Display is successful in these endeavors, it could entice TV brands that haven't previously developed OLED TVs to finally take the plunge.</p><div><blockquote><p>If LG Display is successful in these endeavors, it could entice TV brands that haven't previously developed OLED TVs to finally take the plunge.</p></blockquote></div><p>In 2027 and beyond, OLED TVs could see even cheaper pricing, provided these advancements aren't offset by additional supply-chain shortages. </p><p>It's possible that the next major OLED frontier <em>won't</em> be about making these TVs bigger and brighter, but rather, leaner and cheaper.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.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/tvs/4k-tvs/rgb-led-vs-tandem-oled-tvs-weve-tested-both-and-here-are-the-differences">RGB LED vs Tandem OLED TVs: We’ve tested both and here are the differences</a></li><li><a href="https://www.tomsguide.com/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up">We just graded every TV brand on price, performance and more — here’s how their report cards stack up</a></li><li><a href="https://www.tomsguide.com/tvs/tcl-and-sony-are-joining-forces-to-sell-more-tvs-but-is-that-bad-news-for-shoppers">TCL and Sony are joining forces to sell more TVs — but is that bad news for shoppers?</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ PlayStation’s 27-inch gaming monitor is perfect for PS5 and PC gamers — here’s why ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The PlayStation 27" Gaming Monitor bridges the worlds of console and PC gaming. While the <a href="https://www.tomsguide.com/reviews/ps5">PS5</a> is primarily used as a living room device, many prefer connecting Sony’s console to one of the <a href="https://www.tomsguide.com/us/best-gaming-monitors,review-3114.html">best gaming monitors</a> and playing at their desk. Now they can do so with a monitor designed specifically for the PS5 that’s also compatible with the <a href="https://www.tomsguide.com/us/best-gaming-pc,review-2219.html">best gaming PCs</a>.</p><p>This 27-inch monitor covers the basics, including a 1440p IPS panel with a variable 240Hz refresh rate. The design is fairly nondescript, with a black-and-white theme that matches the PS5. It also includes unique features, such as Auto HDR tone-mapping that calibrates your PS5’s video settings and a convenient charging hook for your <a href="https://www.tomsguide.com/news/ps5-dualsense-controller">DualSense controller</a>.</p><p>While the PlayStation 27-inch gaming monitor isn’t revolutionary, it delivers on its promise as an excellent desktop companion for your PS5. Sure, it doesn’t have 4K resolution or an OLED panel, but it still offers a fantastic gaming experience for only $349. If you’re looking for a monitor for both your PC and PS5, this monitor comes with my highest recommendation. Find out why in my full review.</p><h3 class="article-body__section" id="section-playstation-27-gaming-monitor-review-cheat-sheet"><span>PlayStation 27" Gaming Monitor review: Cheat sheet</span></h3><ul><li><strong>What is it?</strong> A 27-inch gaming monitor that’s specifically designed for the PS5 and PS5 Pro.</li><li><strong>Who is it for?</strong> For gamers who own a PS5 and/or a gaming PC and prefer playing at their desk.</li><li><strong>What does it cost?</strong> The PlayStation 27” Gaming Monitor is available now for $349 on <a href="https://direct.playstation.com/en-us/buy-accessories/27-gaming-monitor-with-dualsense-charging-hook" target="_blank" rel="nofollow">PlayStation’s website</a> and at <a href="https://www.bestbuy.com/product/sony-interactive-entertainment-playstation-27-gaming-monitor-with-dualsense-charging-hook-qhd-black/JXHQ37TRKF/sku/6675156" target="_blank" rel="nofollow">Best Buy</a>.</li><li><strong>What do we like?</strong> We like the sleek, minimalist design, bright and colorful display, fast performance, built-in charging hook, and Auto HDR tone mapping.</li><li><strong>What don’t we like?</strong> We don’t like that it tops out at 1440p and lacks an OLED panel.</li></ul><h3 class="article-body__section" id="section-playstation-27-gaming-monitor-review-specs"><span>PlayStation 27" Gaming Monitor review: Specs</span></h3><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>PlayStation 27 Gaming Monitor</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p>$349</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>27-inch</p></td></tr><tr><td class="firstcol " ><p><strong>Resolution</strong></p></td><td  ><p>2560 x 1440</p></td></tr><tr><td class="firstcol " ><p><strong>Aspect ratio</strong></p></td><td  ><p>16:9</p></td></tr><tr><td class="firstcol " ><p><strong>Refresh rate</strong></p></td><td  ><p>60Hz - 240Hz</p></td></tr><tr><td class="firstcol " ><p><strong>Panel type</strong></p></td><td  ><p>IPS</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>2x HDMI, 1x DisplayPort, 2x USB</p></td></tr></tbody></table></div><h3 class="article-body__section" id="section-playstation-27-gaming-monitor-review-the-ups"><span>PlayStation 27" gaming monitor review: The ups</span></h3><p>The 27-inch PlayStation gaming monitor impresses thanks to its elegant design, vibrant picture quality, smooth performance, and PS5-centric features.</p><h2 id="ps5-like-design">PS5-like design</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="aRuzMYXoAco7rWvAWij3FZ" name="PlayStation 27-inch gaming monitor-6" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/aRuzMYXoAco7rWvAWij3FZ-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The first thing you’ll notice is that this looks a lot like a PlayStation monitor, thanks to its black-and-white color scheme. The almost all-white back, featuring the iconic PlayStation logo, complements the black shaft, stand, bezels, and the port locations. It’s a classy design that looks just as good in an office as in a gaming den.</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="Jr8oH5HRipZow2jLMZg5EQ" name="PlayStation 27-inch gaming monitor-18" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/Jr8oH5HRipZow2jLMZg5EQ-1920-80.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>Gliding the monitor up and down the shaft is easy, as is tilting and swiveling it to your preferred viewing height and angle. The shaft is completely flat and takes up little space on your desk, leaving more room for peripherals. There’s also a VESA mount on the back to use it with one of the <a href="https://www.tomsguide.com/best-picks/best-monitor-arms">best monitor arms</a>, if you prefer.</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="ar5Jh4W4M2kNxsWeVeTxdT" name="PlayStation 27-inch gaming monitor-14" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/ar5Jh4W4M2kNxsWeVeTxdT-1920-80.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>Port-wise, you get everything you need to connect to a PS5 and/or PC. This includes a pair of HDMI 2.1 ports, a single DisplayPort, two USB-A ports, a USB-C port, and a USB-B port. Since the monitor is so light, it’s easy to turn it around to access the ports. It’s nearly impossible to connect anything by touch alone if you’re facing the monitor.</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="pYKriHMJatxw3SioG7AAEJ" name="PlayStation 27-inch gaming monitor-12" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/pYKriHMJatxw3SioG7AAEJ-1920-80.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>My favorite design element is the charging hook for your controller. The hook swings down from the back of the monitor like a door handle and snaps into place when fully down or fully up. There’s just enough room on the hook’s end for your DualSense controller to rest comfortably while it charges. This is one of the smartest features I’ve seen on a monitor.</p><h2 id="vivid-and-sharp-display">Vivid and sharp display</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="DVc7CbqsstmYGre7iMizte" name="PlayStation 27-inch gaming monitor-2" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/DVc7CbqsstmYGre7iMizte-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The 27-inch PlayStation gaming monitor has a 27-inch (2560 x 1440) IPS panel. Although it’s not an OLED screen, you still get excellent image quality because the monitor automatically calibrates your PS5 for the best HDR settings. The 16:9 aspect ratio is also ideal for PS5 games and many PC titles.</p><p>There are only four standard picture modes: Default, Cool, Warm, and sRGB — with each of these having corresponding HDR modes. Some can look brighter or more colorful, but you generally get the same image quality across the board.</p><p>I was blown away when I tested <a href="https://www.tomsguide.com/reviews/spider-man-2">Marvel’s Spider-Man 2</a> on this monitor in the Default display setting. Because the colors aren’t oversaturated, in-game New York City looked incredibly lifelike and cinematic. Neon signs and energy bolts pop out nicely, while HDR provides strong contrast between dark and light elements. This is as good as it gets for a non-OLED panel.</p><div ><table><caption>Display benchmark results</caption><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>PlayStation 27 gaming monitor</strong></p></th><th  ><p><strong>Alienware AW2726DM</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Nits (brightness)</strong></p></td><td  ><p>297 (SDR) | 339 (HDR)</p></td><td  ><p>181 (SDR) | 182 (HDR)</p></td></tr><tr><td class="firstcol " ><p><strong>sRGB</strong></p></td><td  ><p>136.3%</p></td><td  ><p>190%</p></td></tr><tr><td class="firstcol " ><p><strong>DCI-P3</strong></p></td><td  ><p>96.9%</p></td><td  ><p>140.9%</p></td></tr><tr><td class="firstcol " ><p><strong>Delta-E</strong></p></td><td  ><p>0.32</p></td><td  ><p>0.24</p></td></tr></tbody></table></div><p>The 27-inch PlayStation monitor did well on our lab test. Color reproduction (sRGB and DCI-P3) is excellent, while color accuracy (Delta-E) is just slightly lower than what I usually see from gaming monitors. In contrast, the $349 <a href="https://www.tomsguide.com/computing/monitors/alienware-aw2726dm-review">Alienware AW2726DM</a> delivers much more vivid colors, but its HDR across 100% of the screen can’t match the PlayStation 27’s. The latter is certainly a benefit of lacking an OLED panel.</p><h2 id="smooth-performance">Smooth performance</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="jNgxgBT5bhFcMTyoxcVkAk" name="PlayStation 27-inch gaming monitor-3" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/jNgxgBT5bhFcMTyoxcVkAk-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The PlayStation 27-inch gaming monitor supports up to a 240Hz refresh rate, double the PS5’s maximum 120Hz. That means that whether you’re playing on a PS5 or PC, you’ll get a buttery-smooth gaming experience thanks to the high refresh rate and VRR support.</p><p>Spider-Man 2 on the PS5 Pro runs like a dream on this monitor. I didn’t experience a single instance of screen tearing or input lag. This is especially important in a game like this, where you’re swinging through Manhattan’s artificial canyons at breakneck speeds one moment, and dealing with mobs of enemies the next. No matter what’s happening on the screen, the monitor handles it beautifully.</p><h2 id="simple-interface">Simple interface</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="rs2oP8uLpRBVddB3ZD8cb3" name="PlayStation 27-inch gaming monitor-9" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/rs2oP8uLpRBVddB3ZD8cb3-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The menu UI’s design is as clean and simple as the monitor itself. The interface features a transparent background with white lettering. Here, you can access the aforementioned picture modes, as well as settings for audio, inputs, profiles, and more. </p><p>The OSD (On-Screen Display) joystick on the back lets you navigate the menus easily. Located just above the power button, the white joystick blends well with the back of the monitor and offers the right balance of sturdiness and flexibility when you move it up and down and left and right.</p><h3 class="article-body__section" id="section-playstation-27-gaming-monitor-review-the-downs"><span>PlayStation 27" Gaming Monitor review: The downs</span></h3><p>While there’s a lot to like about this PlayStation monitor, it has a few minor drawbacks.</p><h2 id="no-oled-or-4k">No OLED or 4K</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="8KdrpzRQcrmNzMzjga3S3A" name="PlayStation 27-inch gaming monitor-8" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/8KdrpzRQcrmNzMzjga3S3A-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The only two real negatives about the 27-inch PlayStation gaming monitor are that it doesn’t have an OLED panel and it tops out at 1440p. If you absolutely need an OLED monitor or 4K resolution, then this could be a major deterrent. However, if that doesn’t matter to you, you might not see these as negatives at all.</p><p>I’m mostly sharing this as an expectation check, because while it’s a bummer that the display isn’t OLED and the resolution is 1440p tops, I don’t consider these major deal-breakers. The overall image quality is phenomenal, and arguably better than what you’d get on some similarly-priced monitors.</p><p>As for price, the $349 price tag makes it extremely easy to forgive any shortcomings, especially when its upsides vastly outweigh the negatives.</p><h3 class="article-body__section" id="section-playstation-27-gaming-monitor-review-verdict"><span>PlayStation 27" gaming monitor review: Verdict</span></h3><p>The PlayStation 27-inch gaming monitor is a worthwhile purchase for gamers who primarily play at their desk. Though it’s not the fanciest gaming monitor out there, it has everything you need for a great experience on PS5 and PC. And as I said before, it’s hard to knock this monitor because of its affordable $349 asking price.</p><p>If you absolutely need an OLED monitor, the $349 Alienware AW2726DM is a solid alternative. But if you want seamless integration with your PS5, plus a charging hook for your controller, the 27-inch PlayStation is an easy recommendation. It’s really that good!</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/gaming/playstation/playstation-27-gaming-monitor-review</link>
                                                                            <description>
                            <![CDATA[ PlayStation’s 27-inch gaming monitor is the perfect companion for the PS5 thanks to its rich picture quality, smooth performance, and unique features. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">RdLwsWXepmUZUqp8aH49Pe</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/mdQMiYezMUv39JigPi7jhR-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 25 Aug 2026 13:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Monitors]]></category>
                                                    <category><![CDATA[Playstation]]></category>
                                                    <category><![CDATA[Computing]]></category>
                                                    <category><![CDATA[Computing Hardware]]></category>
                                                    <category><![CDATA[Gaming]]></category>
                                                                                                <author><![CDATA[ tony.polanco@futurenet.com (Tony Polanco) ]]></author>                    <dc:creator><![CDATA[ Tony Polanco ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/atzRNqFt5wYgEUPBDahWsD-320-70.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/mdQMiYezMUv39JigPi7jhR-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[PlayStation 27&quot; Gaming Monitor]]></media:description>                                                            <media:text><![CDATA[PlayStation 27&quot; Gaming Monitor]]></media:text>
                                <media:title type="plain"><![CDATA[PlayStation 27&quot; Gaming Monitor]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/mdQMiYezMUv39JigPi7jhR-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The PlayStation 27" Gaming Monitor bridges the worlds of console and PC gaming. While the <a href="https://www.tomsguide.com/reviews/ps5">PS5</a> is primarily used as a living room device, many prefer connecting Sony’s console to one of the <a href="https://www.tomsguide.com/us/best-gaming-monitors,review-3114.html">best gaming monitors</a> and playing at their desk. Now they can do so with a monitor designed specifically for the PS5 that’s also compatible with the <a href="https://www.tomsguide.com/us/best-gaming-pc,review-2219.html">best gaming PCs</a>.</p><p>This 27-inch monitor covers the basics, including a 1440p IPS panel with a variable 240Hz refresh rate. The design is fairly nondescript, with a black-and-white theme that matches the PS5. It also includes unique features, such as Auto HDR tone-mapping that calibrates your PS5’s video settings and a convenient charging hook for your <a href="https://www.tomsguide.com/news/ps5-dualsense-controller">DualSense controller</a>.</p><p>While the PlayStation 27-inch gaming monitor isn’t revolutionary, it delivers on its promise as an excellent desktop companion for your PS5. Sure, it doesn’t have 4K resolution or an OLED panel, but it still offers a fantastic gaming experience for only $349. If you’re looking for a monitor for both your PC and PS5, this monitor comes with my highest recommendation. Find out why in my full review.</p><h3 class="article-body__section" id="section-playstation-27-gaming-monitor-review-cheat-sheet"><span>PlayStation 27" Gaming Monitor review: Cheat sheet</span></h3><ul><li><strong>What is it?</strong> A 27-inch gaming monitor that’s specifically designed for the PS5 and PS5 Pro.</li><li><strong>Who is it for?</strong> For gamers who own a PS5 and/or a gaming PC and prefer playing at their desk.</li><li><strong>What does it cost?</strong> The PlayStation 27” Gaming Monitor is available now for $349 on <a href="https://direct.playstation.com/en-us/buy-accessories/27-gaming-monitor-with-dualsense-charging-hook" target="_blank" rel="nofollow">PlayStation’s website</a> and at <a href="https://www.bestbuy.com/product/sony-interactive-entertainment-playstation-27-gaming-monitor-with-dualsense-charging-hook-qhd-black/JXHQ37TRKF/sku/6675156" target="_blank" rel="nofollow">Best Buy</a>.</li><li><strong>What do we like?</strong> We like the sleek, minimalist design, bright and colorful display, fast performance, built-in charging hook, and Auto HDR tone mapping.</li><li><strong>What don’t we like?</strong> We don’t like that it tops out at 1440p and lacks an OLED panel.</li></ul><h3 class="article-body__section" id="section-playstation-27-gaming-monitor-review-specs"><span>PlayStation 27" Gaming Monitor review: Specs</span></h3><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>PlayStation 27 Gaming Monitor</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p>$349</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>27-inch</p></td></tr><tr><td class="firstcol " ><p><strong>Resolution</strong></p></td><td  ><p>2560 x 1440</p></td></tr><tr><td class="firstcol " ><p><strong>Aspect ratio</strong></p></td><td  ><p>16:9</p></td></tr><tr><td class="firstcol " ><p><strong>Refresh rate</strong></p></td><td  ><p>60Hz - 240Hz</p></td></tr><tr><td class="firstcol " ><p><strong>Panel type</strong></p></td><td  ><p>IPS</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>2x HDMI, 1x DisplayPort, 2x USB</p></td></tr></tbody></table></div><h3 class="article-body__section" id="section-playstation-27-gaming-monitor-review-the-ups"><span>PlayStation 27" gaming monitor review: The ups</span></h3><p>The 27-inch PlayStation gaming monitor impresses thanks to its elegant design, vibrant picture quality, smooth performance, and PS5-centric features.</p><h2 id="ps5-like-design">PS5-like design</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="aRuzMYXoAco7rWvAWij3FZ" name="PlayStation 27-inch gaming monitor-6" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/aRuzMYXoAco7rWvAWij3FZ-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The first thing you’ll notice is that this looks a lot like a PlayStation monitor, thanks to its black-and-white color scheme. The almost all-white back, featuring the iconic PlayStation logo, complements the black shaft, stand, bezels, and the port locations. It’s a classy design that looks just as good in an office as in a gaming den.</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="Jr8oH5HRipZow2jLMZg5EQ" name="PlayStation 27-inch gaming monitor-18" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/Jr8oH5HRipZow2jLMZg5EQ-1920-80.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>Gliding the monitor up and down the shaft is easy, as is tilting and swiveling it to your preferred viewing height and angle. The shaft is completely flat and takes up little space on your desk, leaving more room for peripherals. There’s also a VESA mount on the back to use it with one of the <a href="https://www.tomsguide.com/best-picks/best-monitor-arms">best monitor arms</a>, if you prefer.</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="ar5Jh4W4M2kNxsWeVeTxdT" name="PlayStation 27-inch gaming monitor-14" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/ar5Jh4W4M2kNxsWeVeTxdT-1920-80.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>Port-wise, you get everything you need to connect to a PS5 and/or PC. This includes a pair of HDMI 2.1 ports, a single DisplayPort, two USB-A ports, a USB-C port, and a USB-B port. Since the monitor is so light, it’s easy to turn it around to access the ports. It’s nearly impossible to connect anything by touch alone if you’re facing the monitor.</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="pYKriHMJatxw3SioG7AAEJ" name="PlayStation 27-inch gaming monitor-12" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/pYKriHMJatxw3SioG7AAEJ-1920-80.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>My favorite design element is the charging hook for your controller. The hook swings down from the back of the monitor like a door handle and snaps into place when fully down or fully up. There’s just enough room on the hook’s end for your DualSense controller to rest comfortably while it charges. This is one of the smartest features I’ve seen on a monitor.</p><h2 id="vivid-and-sharp-display">Vivid and sharp display</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="DVc7CbqsstmYGre7iMizte" name="PlayStation 27-inch gaming monitor-2" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/DVc7CbqsstmYGre7iMizte-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The 27-inch PlayStation gaming monitor has a 27-inch (2560 x 1440) IPS panel. Although it’s not an OLED screen, you still get excellent image quality because the monitor automatically calibrates your PS5 for the best HDR settings. The 16:9 aspect ratio is also ideal for PS5 games and many PC titles.</p><p>There are only four standard picture modes: Default, Cool, Warm, and sRGB — with each of these having corresponding HDR modes. Some can look brighter or more colorful, but you generally get the same image quality across the board.</p><p>I was blown away when I tested <a href="https://www.tomsguide.com/reviews/spider-man-2">Marvel’s Spider-Man 2</a> on this monitor in the Default display setting. Because the colors aren’t oversaturated, in-game New York City looked incredibly lifelike and cinematic. Neon signs and energy bolts pop out nicely, while HDR provides strong contrast between dark and light elements. This is as good as it gets for a non-OLED panel.</p><div ><table><caption>Display benchmark results</caption><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>PlayStation 27 gaming monitor</strong></p></th><th  ><p><strong>Alienware AW2726DM</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Nits (brightness)</strong></p></td><td  ><p>297 (SDR) | 339 (HDR)</p></td><td  ><p>181 (SDR) | 182 (HDR)</p></td></tr><tr><td class="firstcol " ><p><strong>sRGB</strong></p></td><td  ><p>136.3%</p></td><td  ><p>190%</p></td></tr><tr><td class="firstcol " ><p><strong>DCI-P3</strong></p></td><td  ><p>96.9%</p></td><td  ><p>140.9%</p></td></tr><tr><td class="firstcol " ><p><strong>Delta-E</strong></p></td><td  ><p>0.32</p></td><td  ><p>0.24</p></td></tr></tbody></table></div><p>The 27-inch PlayStation monitor did well on our lab test. Color reproduction (sRGB and DCI-P3) is excellent, while color accuracy (Delta-E) is just slightly lower than what I usually see from gaming monitors. In contrast, the $349 <a href="https://www.tomsguide.com/computing/monitors/alienware-aw2726dm-review">Alienware AW2726DM</a> delivers much more vivid colors, but its HDR across 100% of the screen can’t match the PlayStation 27’s. The latter is certainly a benefit of lacking an OLED panel.</p><h2 id="smooth-performance">Smooth performance</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="jNgxgBT5bhFcMTyoxcVkAk" name="PlayStation 27-inch gaming monitor-3" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/jNgxgBT5bhFcMTyoxcVkAk-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The PlayStation 27-inch gaming monitor supports up to a 240Hz refresh rate, double the PS5’s maximum 120Hz. That means that whether you’re playing on a PS5 or PC, you’ll get a buttery-smooth gaming experience thanks to the high refresh rate and VRR support.</p><p>Spider-Man 2 on the PS5 Pro runs like a dream on this monitor. I didn’t experience a single instance of screen tearing or input lag. This is especially important in a game like this, where you’re swinging through Manhattan’s artificial canyons at breakneck speeds one moment, and dealing with mobs of enemies the next. No matter what’s happening on the screen, the monitor handles it beautifully.</p><h2 id="simple-interface">Simple interface</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="rs2oP8uLpRBVddB3ZD8cb3" name="PlayStation 27-inch gaming monitor-9" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/rs2oP8uLpRBVddB3ZD8cb3-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The menu UI’s design is as clean and simple as the monitor itself. The interface features a transparent background with white lettering. Here, you can access the aforementioned picture modes, as well as settings for audio, inputs, profiles, and more. </p><p>The OSD (On-Screen Display) joystick on the back lets you navigate the menus easily. Located just above the power button, the white joystick blends well with the back of the monitor and offers the right balance of sturdiness and flexibility when you move it up and down and left and right.</p><h3 class="article-body__section" id="section-playstation-27-gaming-monitor-review-the-downs"><span>PlayStation 27" Gaming Monitor review: The downs</span></h3><p>While there’s a lot to like about this PlayStation monitor, it has a few minor drawbacks.</p><h2 id="no-oled-or-4k">No OLED or 4K</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="8KdrpzRQcrmNzMzjga3S3A" name="PlayStation 27-inch gaming monitor-8" alt="PlayStation 27" Gaming Monitor" src="https://cdn.mos.cms.futurecdn.net/8KdrpzRQcrmNzMzjga3S3A-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The only two real negatives about the 27-inch PlayStation gaming monitor are that it doesn’t have an OLED panel and it tops out at 1440p. If you absolutely need an OLED monitor or 4K resolution, then this could be a major deterrent. However, if that doesn’t matter to you, you might not see these as negatives at all.</p><p>I’m mostly sharing this as an expectation check, because while it’s a bummer that the display isn’t OLED and the resolution is 1440p tops, I don’t consider these major deal-breakers. The overall image quality is phenomenal, and arguably better than what you’d get on some similarly-priced monitors.</p><p>As for price, the $349 price tag makes it extremely easy to forgive any shortcomings, especially when its upsides vastly outweigh the negatives.</p><h3 class="article-body__section" id="section-playstation-27-gaming-monitor-review-verdict"><span>PlayStation 27" gaming monitor review: Verdict</span></h3><p>The PlayStation 27-inch gaming monitor is a worthwhile purchase for gamers who primarily play at their desk. Though it’s not the fanciest gaming monitor out there, it has everything you need for a great experience on PS5 and PC. And as I said before, it’s hard to knock this monitor because of its affordable $349 asking price.</p><p>If you absolutely need an OLED monitor, the $349 Alienware AW2726DM is a solid alternative. But if you want seamless integration with your PS5, plus a charging hook for your controller, the 27-inch PlayStation is an easy recommendation. It’s really that good!</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Tom’s Guide Computing Awards 2026: 22 game-changing laptops, desktops and peripherals ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Let’s be honest: between soaring memory prices and the endless barrage of tech buzzwords, building the ultimate setup in 2026 hasn't been easy. But amidst the chaos, hardware makers gave us some of the most genuinely exciting, boundary-pushing gear we’ve seen in a decade.</p><p>This year, "compromise" became a dirty word. Laptops gained dual OLED screens without losing all-day battery life, budget peripherals delivered soundscapes usually reserved for four-figure studio setups, and on-device processing finally evolved from a gimmick into everyday horsepower.</p><p>We sifted through the noise, ran hundreds of lab benchmarks, and tested dozens of contenders to death. Here are the 22 standout winners that earned a permanent place on our desks for the 2026 Tom’s Guide Computing Awards.</p><h2 class="article-body__section" id="section-laptops"><span>Laptops</span></h2><h2 id="dell-xps-13">Dell XPS 13</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NCCtjR5QsyRt2m9F8NffYJ" name="Dell XPS 13" alt="Dell XPS 13" src="https://cdn.mos.cms.futurecdn.net/NCCtjR5QsyRt2m9F8NffYJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/laptops/dell-xps-13-2026-review">Dell XPS 13</a> is a true MacBook Neo rival and easily the best <a href="https://www.tomsguide.com/best-picks/best-budget-laptops">budget laptop</a> you can buy right now. Despite its low $699 starting price, it doesn’t skimp on luxury features you’d find on more expensive laptops, giving you quality with few compromises.</p><p>The thin, light aluminum chassis not only looks great, but it also makes the XPS 13 ideal for travel. The sharp 13.4-inch 2K touch display is bright and colorful, while the Intel Wildcat Lake chip inside gives you plenty of performance for everyday work. And if that wasn’t enough, it has more than enough battery life to last you for a full workday.<em> — Tony Polanco, Senior Writer</em></p><h2 id="asus-zenbook-duo-2026">Asus Zenbook Duo (2026)</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="H4NkCprHbHk7yqDEd5phpJ" name="ASUS Zenbook DUO (UX8407)" alt="ASUS Zenbook DUO (UX8407)" src="https://cdn.mos.cms.futurecdn.net/H4NkCprHbHk7yqDEd5phpJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>Dual-screen laptops have always been a bit gimmicky to me, but this year’s <a href="https://www.tomsguide.com/computing/laptops/asus-zenbook-duo-2026-review">Asus Zenbook Duo</a> proved me dead wrong with its potent combination of two mesmerizing 14-inch OLED panels and the raw performance of Intel’s breakthrough Core Ultra X9 388H chipset.</p><p>It all starts with a ground-up ceraluminum redesign for the double display system that feels premium to the touch and sturdier against the elements. Throw in a CPU that has the power of a supercar paired with the efficiency of a Toyota Prius (you can actually game on this thing, and it has a 14-hour battery life), and it’s easily one of my favorite laptops of the year. — <em>Jason England, Managing Editor of Core Tech</em></p><h2 id="acer-predator-helios-18-ai">Acer Predator Helios 18 AI</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="eU59CembDesdgQWsb4Nvz6" name="Acer Predator Helios 18 AI" alt="Acer Predator Helios 18 AI" src="https://cdn.mos.cms.futurecdn.net/eU59CembDesdgQWsb4Nvz6-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>But what about in the gaming laptop space? There’s been a whole lot of monsters born this year, and Acer’s Predator Helios 18 AI definitely sits in that portable desktop replacement camp with what’s lying under the hood (up to an RTX 5090) and the massive wattage being pumped through to the chip.</p><p>Combine that with a gorgeously massive and buttery-smooth mini-LED panel, up to 192GB of RAM (in this economy!?) and a strong keyboard typing experience with plenty of ports, and you’ve got quite a winning formula. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 id="razer-blade-16-2026">Razer Blade 16 (2026)</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="es2oB6CoGAC4NyZCi9xtSJ" name="Razer Blade 16 (2026)" alt="Razer Blade 16 (2026)" src="https://cdn.mos.cms.futurecdn.net/es2oB6CoGAC4NyZCi9xtSJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/gaming-laptops/razer-blade-16-2026-review">Razer Blade 16 (2026)</a> is an ultraportable gaming laptop that makes no compromises. Thanks to its powerful components, it can run even the most demanding PC games at buttery-smooth frame rates. The vivid 16-inch OLED panel ensures all those games are a sight to behold.</p><p>The sleek design, with its sturdy, CNC-milled, all-black aluminum chassis, means this laptop will look just as great in the office as it is in your gaming den. Of course, RGB lighting on the keyboard deck shows the Blade 16 knows how to have fun. Though certainly pricey at $3,599 (to start), this is one of the finest gaming laptops I’ve reviewed and is worth your hard-earned cash.<em> — Tony Polanco, Senior Writer</em></p><h2 id="acer-chromebook-plus-spin-514">Acer Chromebook Plus Spin 514</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="tQ5vL2upFfZrpELPcFhtyJ" name="Acer Chromebook Plus Spin 514" alt="Acer Chromebook Plus Spin 514" src="https://cdn.mos.cms.futurecdn.net/tQ5vL2upFfZrpELPcFhtyJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>When MediaTek jumped into the Chromebook pool with its Kompanio Ultra ARM silicon, it was like Google’s laptops got a shot in the…well, arm. And of all the options released, the <a href="https://www.tomsguide.com/computing/chromebooks/acer-chromebook-plus-spin-514-review">Acer Chromebook Plus Spin 514</a> stands out as one of the most impressive.</p><p>Because it’s not just about the performance and power efficiency (almost 15 hours of battery life on one charge) or AI-infused simplicity of a Gemini-boosted Chrome OS. It’s about the experience you build around them, and this OLED 2-in-1 is one of the nicest-feeling Chromebooks I’ve touched. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 class="article-body__section" id="section-tablets"><span>Tablets</span></h2><h2 id="lenovo-tab-plus-gen-2">Lenovo Tab Plus Gen 2</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jboXFMxD4Wz4UV9FQF7UfJ" name="Lenovo Tab Plus Gen 2" alt="Lenovo Tab Plus Gen 2" src="https://cdn.mos.cms.futurecdn.net/jboXFMxD4Wz4UV9FQF7UfJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/android-tablets/the-gargantuan-speaker-on-this-android-tablet-isnt-a-gimmick-i-cant-believe-how-good-it-sounds">Lenovo Tab Plus Gen 2</a> is an entertainment-focused Android tablet that has the largest speaker I’ve ever seen. The JBL speaker is so large, you could even say it’s a speaker with a tablet attached, rather than the other way around. And it’s not for show either, as it delivers punchy, bassy sound you can feel in your chest. You can even use it as a Bluetooth speaker, which adds versatility.</p><p>The 12.1-inch IPS display is bright and colorful, making whatever you’re watching look its best. The 16:9 aspect ratio is great for videos, and it also works well for comic books and websites in vertical mode. There’s also a handy built-in kickstand when you want to sit back and relax at your desk. Though its performance isn’t the best for getting work done and it's too heavy to hold for long periods, the incredible sound and overall ease of use make this $399 tablet an easy recommendation.<em> — Tony Polanco, Senior Writer</em></p><h2 class="article-body__section" id="section-desktops"><span>Desktops</span></h2><h2 id="alienware-area-51-desktop">Alienware Area-51 Desktop</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Wb4EEwvgsSwqGQqgd3DgQJ" name="Alienware Area-51 Desktop" alt="Alienware Area-51 Desktop" src="https://cdn.mos.cms.futurecdn.net/Wb4EEwvgsSwqGQqgd3DgQJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/gaming-desktops/alienware-area-51-review">Alienware Area-51</a> is for hardcore users willing to pay top dollar for premium gaming performance. Aside from AMD Ryzen X3D processor options, this is effectively the same PC I reviewed last year. That’s not a bad thing, as this is still a phenomenal PC.</p><p>You can configure this beast with up to an Nvidia GeForce RTX 5090 GPU, 4TB of storage, and 64GB of RAM. You can also kit it out with an Intel Core Ultra 200S chip if you’re Team Blue. All of it is housed in an otherworldly design that keeps the PC running cool and quiet. If you have the space for it in your office or gaming den, this is the ultimate gaming PC.<em> — Tony Polanco, Senior Writer</em></p><h2 id="lenovo-yoga-mini">Lenovo Yoga Mini</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Xx4tn6EnLFYk6Tg3cCBo3K" name="Lenovo Yoga Mini i (1L, 11)" alt="Lenovo Yoga Mini i (1L, 11)" src="https://cdn.mos.cms.futurecdn.net/Xx4tn6EnLFYk6Tg3cCBo3K-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/desktop-computers/mini-pcs/lenovo-yoga-mini-review">Lenovo Yoga Mini</a> breaks free from the rectangular design of other mini PCs, adding cool extras like RGB underglow and touch controls. This circular mini PC is meant to sit front and center on your desk, and it looks absolutely stunning. </p><p>Despite its smaller size and unique shape, the Yoga Mini is equipped with Intel’s latest Ultra X5 and X7 chips to handle whatever you throw at it. Lenovo didn’t skimp on ports either, and you also get an integrated fingerprint scanner in the power button for secure and seamless logins. If you’re after a small yet unique mini PC that stands out from the crowd, this is one of the most impressive machines and designs I’ve seen yet. — <em>Anthony Spadafora, Managing Editor</em></p><h2 id="asus-rog-g1000">Asus ROG G1000</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="VCKwmG7G3htVPyChwrE8wJ" name="Asus ROG G1000" alt="Asus ROG G1000" src="https://cdn.mos.cms.futurecdn.net/VCKwmG7G3htVPyChwrE8wJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>So I’ll just say it upfront — Asus has created a monster that’s not just designed to be played, but to be seen as well. This is a monolithic pre-built tower that packs a whole bunch of flash courtesy of that holographic fan giving it a real stage presence atop your desk.</p><p>This is what happens when a company throws out the game plan and decides to do something a bit bonkers. Under the hood, you can spec it up to an AMD Ryzen 9 9950X3D2, an RTX 5090, a whopping 128GB of DDR5 RAM, and a 4TB SSD — complete with a 1,000-watt power supply keeping it all going. Top it off with some real cooling innovation with that Tri-zone airflow design, and this beast won’t break a sweat. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 id="khadas-mind-pro">Khadas Mind Pro</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:6589px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XHow7PCtKGK27essMZjZfU" name="Khadas Mind Pro Modular Mini PC" alt="Khadas Mind Pro Modular Mini PC" src="https://cdn.mos.cms.futurecdn.net/XHow7PCtKGK27essMZjZfU-1920-80.jpg" mos="" align="middle" fullscreen="" width="6589" height="3706" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Khadas)</span></figcaption></figure><p>Khadas continues to build on its modular mini PC ecosystem with its new Mind Pro. Although this portable mini PC packs in more power than ever before, it’s not that much larger than the original Mind or the Mind 2S. Likewise, thanks to Khadas’ proprietary Mind Connector on the bottom, it’s still compatible with the company’s existing docks, which instantly transform its mini PCs into a gaming rig or even a laptop.</p><p>The Mind Pro may be small, but like its predecessors, it still comes packed with full-sized ports, including dual Thunderbolt 4 ports. There’s also a 7.98Wh battery inside that lets this more powerful mini PC sleep for over 48 hours, so you can bring it from one workstation or location to another. The Mind Pro’s extra thickness over the Mind is certainly worth it if you want even more power in a similar form factor with a growing ecosystem of game-changing accessories. <em>— Anthony Spadafora, Managing Editor</em></p><h2 class="article-body__section" id="section-monitors"><span>Monitors</span></h2><h2 id="msi-mpg-341cqr-qd-oled-x36">MSI MPG 341CQR QD-OLED X36</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZoLiWyUquxJDjJUYGMscjJ" name="MSI MPG 341CQR QD-OLED X36" alt="MSI MPG 341CQR QD-OLED X36" src="https://cdn.mos.cms.futurecdn.net/ZoLiWyUquxJDjJUYGMscjJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>MSI knows how to make a great gaming monitor, and the <a href="https://www.tomsguide.com/computing/monitors/msi-mpg-341cqr-qd-oled-x36-review">MPG 341CQR QD-OLED X36</a> is one of its best so far — and certainly the best this year. A blistering 360Hz refresh rate and 0.03ms response time deliver buttery-smooth gameplay.</p><p>The monitor also boasts phenomenal color coverage and accuracy, making it ideal not just for gaming, but for professional color grading and editing too. The X36 also features an AI Care Sensor that automatically alters the monitor’s display settings by detecting a human in front of it — yes, AI isn’t just a gimmick on the X36. — <em>Nikita Achanta, Senior Reviews Writer</em></p><h2 id="alienware-27-240hz-qd-oled-gaming-monitor-aw2726dm">Alienware 27 240Hz QD-OLED Gaming Monitor (AW2726DM)</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="WJT4zMwdFkoMT3Lz7aZErJ" name="Alienware 27 240Hz QD-OLED Gaming Monitor (AW2726DM)" alt="Alienware 27 240Hz QD-OLED Gaming Monitor (AW2726DM)" src="https://cdn.mos.cms.futurecdn.net/WJT4zMwdFkoMT3Lz7aZErJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/monitors/alienware-aw2726dm-review">Alienware AW2726DM</a> is a premium gaming monitor that costs less than you’d expect. For $349, you get a gorgeous 27-inch 1440p OLED panel that makes everything look its best, a blazing-fast 240Hz refresh rate, and a low 0.03ms response time that can keep up with even the fastest-paced games.</p><p>The biggest compliment I can give the AW2726DM is that it has become my main monitor at work, proving that it’s not just good for gaming, as text on websites and documents appears nice and crisp. While HDR brightness could be brighter, and the design is very basic, lacking embellishments like RGB, this is a wonderful gaming monitor that won’t kill your bank account.<em> — Tony Polanco, Senior Writer</em></p><h2 id="samsung-32-inch-odyssey-g8-g80hs">Samsung 32-inch Odyssey G8 G80HS</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="VhhuEVwQAapx6RgAKNgGpJ" name="Samsung 32-inch Odyssey G8 G80HS" alt="Samsung 32-inch Odyssey G8 G80HS" src="https://cdn.mos.cms.futurecdn.net/VhhuEVwQAapx6RgAKNgGpJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/monitors/i-checked-out-the-worlds-first-6k-gaming-monitor-and-its-a-sight-to-behold">Samsung Odyssey 32-inch G8 G80HS</a> is a marvel to behold thanks to its super-sharp 6K resolution, which is a first for gaming monitors, according to Samsung. It offers the most detailed picture quality I’ve ever seen. Trust me when I say that your eyes won’t be disappointed.</p><p>In addition to 6K at 165Hz, you can also switch to 3K mode at 330Hz for fast-paced competitive games. If you have a powerful PC that can play games at frame rates high enough to match that refresh rate, you’re in for a silky smooth and fast experience. While certainly pricey at its usual $1,599 asking price, you’re getting your money’s worth for visual fidelity and performance.<em> — Tony Polanco, Senior Writer</em></p><h2 class="article-body__section" id="section-audio"><span>Audio</span></h2><h2 id="edifier-hecate-g2000-pro">Edifier HECATE G2000 Pro</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="oorkXjCmYZiPZBBAanc6FJ" name="EDIFIER HECATE G2000 Pro" alt="EDIFIER HECATE G2000 Pro" src="https://cdn.mos.cms.futurecdn.net/oorkXjCmYZiPZBBAanc6FJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>Excellent sound quality? On a sub-$200 budget? The <a href="https://www.tomsguide.com/gaming/gaming-peripherals/edifier-g2000-pro-review">Edifier G2000 Pro</a> desktop speakers look straight out of a sci-fi movie, and they’ve got otherworldly sound quality to boot. The reactive RGB lighting dances to in-game sounds and the beat of drums when listening to music.</p><p>The G2000 Pro packs plenty of <em>oomph, </em>delivering deep, meaty bass that makes you feel immersed in RPGs, horror, and racing titles. The treble is bright and crisp, so you can hear the clashing of swords in excruciating detail — and it never becomes fatiguing. In-game dialogue is loud and clear, too, making the G2000 Pro one of the best computer speakers out there. — <em>Nikita Achanta, Senior Reviews Writer</em></p><h2 id="edifier-hecate-g5-max">Edifier HECATE G5 Max</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TApGFAiWFrmHqKR3aymjJJ" name="EDIFIER HECATE G5 Max" alt="EDIFIER HECATE G5 Max" src="https://cdn.mos.cms.futurecdn.net/TApGFAiWFrmHqKR3aymjJJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/gaming/gaming-peripherals/edifier-g5-max-review">Edifier G5 Max </a>makes you feel like the main character without breaking the bank. Equipped with a 53mm dynamic driver, 7.1.4 Spatial Audio co-developed by Hecate and THX, and an extremely comfortable design, the G5 Max also supports Hi-Res Audio streaming.</p><p>Chock-full of features usually reserved for premium prices, the G5 Max goes the extra mile by offering dual apps for customization. It is, by far, the best gaming headset for those on a budget. — <em>Nikita Achanta, Senior Reviews Writer</em></p><h2 id="sony-inzone-h6-air">Sony Inzone H6 Air</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rQ8g6XTnuitSmfLQbhAWgJ" name="Sony INZONE H6 Air" alt="Sony INZONE H6 Air" src="https://cdn.mos.cms.futurecdn.net/rQ8g6XTnuitSmfLQbhAWgJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/gaming/gaming-peripherals/sony-inzone-h6-air-review">Sony Inzone H6 Air</a> proves that gaming headsets can be just as good for listening to music. Equipped with 40mm studio-grade drivers found in Sony’s monitoring headphones, the H6 Air is the ideal headset for audiophiles and gamers who want an immersive experience.</p><p>Whether you’re taking in the sights and sounds in open-world RPGs or infiltrating enemy bases in FPS titles, the H6 Air ensures you hear every soft footstep and loud explosion. Its open-back design offers superior comfort for all-day wear, and it comes with a detachable boom mic that makes you sound loud and clear. The H6 Air is the one headset to rule them all. — <em>Nikita Achanta, Senior Reviews Writer</em></p><h2 class="article-body__section" id="section-components"><span>Components</span></h2><h2 id="intel-core-ultra-7-270k-plus">Intel Core Ultra 7 270K Plus</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2Fd3Ep2gWWzy89oqbXRzoJ" name="Intel Core Ultra 7 270K" alt="Intel Core Ultra 7 270K Plus" src="https://cdn.mos.cms.futurecdn.net/2Fd3Ep2gWWzy89oqbXRzoJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>Intel’s been on a bit of a comeback tour this year, and the <a href="https://www.tomsguide.com/computing/cpus/intel-core-ultra-7-270k-plus-5-250k-plus-review">Core Ultra 7 270K Plus</a> is a highlight of its return to form. By focusing on the fundamentals and righting the wrongs of its previous “Arrow Lake” generation, Intel’s created something special (and well-priced) for your machine.</p><p>The productivity chops are still here — staying miles ahead of AMD in terms of getting stuff done. But with critical improvements to its gaming performance by speeding up the travel time for data inside the chip and offering performance packages, these vastly cheaper chips get uncomfortably close in potential to Team Red’s pricey X3D CPUs. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 id="amd-radeon-rx-9070-gre">AMD Radeon RX 9070 GRE</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Fj5U6sJkWJHWJWKksH8vfJ" name="AMD Radeon RX 9070 GRE" alt="AMD Radeon RX 9070 GRE" src="https://cdn.mos.cms.futurecdn.net/Fj5U6sJkWJHWJWKksH8vfJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>GPU pricing has become a bit of a running joke in 2026, but amid huge price increases, AMD has managed to hold down the fort, with the nicely powerful <a href="https://www.tomsguide.com/computing/gpus/amd-radeon-rx-9070-gre-review">Radeon RX 9070 GRE</a> staying (or even below) its MSRP.</p><p>Built atop AMD’s RDNA 4 architecture to support all that FSR Redstone goodness, this is a fantastic 1440p GPU that offers decent ray tracing performance, strong frame rates, and real value for money. It may not be the best for productivity, but if you want bang for your buck in gaming, this is solid. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 id="intel-arc-g3-extreme">Intel Arc G3 Extreme</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="wQR38ULkRaxbx9T2BaKgzJ" name="Intel Arc G3 Extreme inside MSI Claw 8 EX AI+" alt="Intel Arc G3 Extreme inside MSI Claw 8 EX AI+" src="https://cdn.mos.cms.futurecdn.net/wQR38ULkRaxbx9T2BaKgzJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>It’s a double win for Intel in the components category, as the Arc G3 Extreme in the <a href="https://www.tomsguide.com/gaming/handheld-gaming/msi-claw-8-ex-ai-plus-review">MSI Claw 8 EX AI+</a> delivers the next-generation breakthrough for gaming handhelds we’ve been waiting for.</p><p>Taking that monster integrated GPU from the Panther Lake laptop chips and stuffing it into a handheld (XeSS 3 multi-frame generation and all) has led to a giant leap not just in frames per second, but in power efficiency too. With just over four hours of battery life on one charge in our gaming testing, and the ability to play games on a 4K screen with ease, this is a phenomenal chip. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 class="article-body__section" id="section-accessories-and-peripherals"><span>Accessories and peripherals</span></h2><h2 id="tessan-voyager-205">Tessan Voyager 205</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hEhVhCtVv4mJhMBMdzo3gJ" name="Tessan Voyager 205" alt="Tessan Voyager 205" src="https://cdn.mos.cms.futurecdn.net/hEhVhCtVv4mJhMBMdzo3gJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>One thing that’s become an increasingly difficult task on my travels is keeping everything powered. The USB-C port demands can create a Frankenstein combination of travel adaptors all over my hotel room, but the <a href="https://www.tomsguide.com/computing/ive-traveled-50-000-miles-with-the-worlds-most-powerful-travel-adaptor-its-the-only-one-i-trust-to-charge-all-my-tech">Tessan Voyager 205</a> has the international compatibility and raw wattage to charge every single thing in my arsenal.</p><p>With three USB-C ports at 45W, three USB-C ports at 160W, USB-A, and a plug adaptor to plug something else in to make the most of that 205W output, this is a compact beast of a charger that keeps everything topped up with ease. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 id="satechi-mac-mini-m4-stand-amp-hub-with-ssd-enclosure">Satechi Mac Mini M4 Stand & Hub with SSD Enclosure</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="s7JNNoDwdBeLbWFSCzokBJ" name="satechi Mac Mini M4 Stand & Hub" alt="satechi Mac Mini M4 Stand & Hub" src="https://cdn.mos.cms.futurecdn.net/s7JNNoDwdBeLbWFSCzokBJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>Satechi has long made some of the best Apple accessories, and its <a href="https://www.tomsguide.com/computing/peripherals/i-added-satechis-new-usb-c-hub-and-stand-to-my-mac-mini-m4-and-its-the-perfect-upgrade">Mac Mini M4 Stand & Hub</a> is the first accessory I’d pick up for Cupertino’s latest mini PC. Thanks to its anodized aluminum finish and dimensions, it complements the Mac mini M4 perfectly while giving you easy access to the device’s power button with a handy cutout on the back.</p><p>With Satechi’s Mac Mini M4 Stand & Hub, you can instantly add three USB-A ports and an SD card reader to your Mac, but that’s not all it can do. There’s also a free M.2 slot on the underside which lets you expand your Mac mini’s storage by up to 4TB. Whether you want to beat the Apple Tax on storage or add extra ports to your Mac mini M4, this accessory is easy to recommend. <em>— Anthony Spadafora, Managing Editor</em></p><h2 id="wobkey-rainy-75-pro">Wobkey Rainy 75 Pro</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YLGD2khjY4m2fGi3krZtYJ" name="Wobkey Rainy 75 Pro" alt="Wobkey Rainy 75 Pro" src="https://cdn.mos.cms.futurecdn.net/YLGD2khjY4m2fGi3krZtYJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>Few keyboards are “designed for audiophiles,” but the <a href="https://www.tomsguide.com/computing/peripherals/wobkey-rainy-75-pro-review">Wobkey Rainy 75 Pro</a> is, and it’s the best-sounding mechanical keyboard I’ve ever tested. Its Kailh Cocoa switches, five layers of sound-dampening foam, and double-shot PBT keycaps come together to create a raindrop-like sound profile that’s gobsmackingly fantastic.</p><p>Plus, the Rainy 75 Pro is an absolute beast for productivity, thanks to its ultra-responsive switches and up to 900 hours of battery life. Its full CNC-machined aluminum construction also means the keyboard can stand the test of time. There really is no better deck for hardcore typists (and fans of a thocky sound). — <em>Nikita Achanta, Senior Reviews Writer</em></p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/computing/toms-guide-computing-awards-2026-22-game-changing-laptops-desktops-and-peripherals</link>
                                                                            <description>
                            <![CDATA[ The Tom’s Guide Computing Awards 2026 are here. Discover the 22 best laptops, desktops, components, and monitors tested and benchmarked by our editors. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">WTapDQdKkp2NFbxpW7VUfF</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/azGg259TfFgWWPzj5ysu2K-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 24 Aug 2026 11:00:00 +0000</pubDate>                                                                                                                                <updated>Fri, 28 Aug 2026 10:50:42 +0000</updated>
                                                                                                                                            <category><![CDATA[Computing]]></category>
                                                                                                                    <dc:creator><![CDATA[ Jason England ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/v4fSq5U4uZUEtGY2BwNuJ6-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Jason brings a decade of tech and gaming journalism experience to his role as a Managing Editor of Computing at Tom&#039;s Guide. He has previously written for Laptop Mag, Tom&#039;s Hardware, Kotaku, Stuff and BBC Science Focus. In his spare time, you&#039;ll find Jason looking for good dogs to pet or thinking about eating pizza if he isn&#039;t already.&lt;/p&gt; ]]></dc:description>
                                                                                                        <dc:contributor><![CDATA[ Anthony Spadafora ]]></dc:contributor>
                                            <dc:contributor><![CDATA[ Tony Polanco ]]></dc:contributor>
                                            <dc:contributor><![CDATA[ Nikita Achanta ]]></dc:contributor>
                                                                    <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/azGg259TfFgWWPzj5ysu2K-1920-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Tom&#039;s Guide Computing Awards 2026]]></media:description>                                                            <media:text><![CDATA[Tom&#039;s Guide Computing Awards 2026]]></media:text>
                                <media:title type="plain"><![CDATA[Tom&#039;s Guide Computing Awards 2026]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/azGg259TfFgWWPzj5ysu2K-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Let’s be honest: between soaring memory prices and the endless barrage of tech buzzwords, building the ultimate setup in 2026 hasn't been easy. But amidst the chaos, hardware makers gave us some of the most genuinely exciting, boundary-pushing gear we’ve seen in a decade.</p><p>This year, "compromise" became a dirty word. Laptops gained dual OLED screens without losing all-day battery life, budget peripherals delivered soundscapes usually reserved for four-figure studio setups, and on-device processing finally evolved from a gimmick into everyday horsepower.</p><p>We sifted through the noise, ran hundreds of lab benchmarks, and tested dozens of contenders to death. Here are the 22 standout winners that earned a permanent place on our desks for the 2026 Tom’s Guide Computing Awards.</p><h2 class="article-body__section" id="section-laptops"><span>Laptops</span></h2><h2 id="dell-xps-13">Dell XPS 13</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NCCtjR5QsyRt2m9F8NffYJ" name="Dell XPS 13" alt="Dell XPS 13" src="https://cdn.mos.cms.futurecdn.net/NCCtjR5QsyRt2m9F8NffYJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/laptops/dell-xps-13-2026-review">Dell XPS 13</a> is a true MacBook Neo rival and easily the best <a href="https://www.tomsguide.com/best-picks/best-budget-laptops">budget laptop</a> you can buy right now. Despite its low $699 starting price, it doesn’t skimp on luxury features you’d find on more expensive laptops, giving you quality with few compromises.</p><p>The thin, light aluminum chassis not only looks great, but it also makes the XPS 13 ideal for travel. The sharp 13.4-inch 2K touch display is bright and colorful, while the Intel Wildcat Lake chip inside gives you plenty of performance for everyday work. And if that wasn’t enough, it has more than enough battery life to last you for a full workday.<em> — Tony Polanco, Senior Writer</em></p><h2 id="asus-zenbook-duo-2026">Asus Zenbook Duo (2026)</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="H4NkCprHbHk7yqDEd5phpJ" name="ASUS Zenbook DUO (UX8407)" alt="ASUS Zenbook DUO (UX8407)" src="https://cdn.mos.cms.futurecdn.net/H4NkCprHbHk7yqDEd5phpJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>Dual-screen laptops have always been a bit gimmicky to me, but this year’s <a href="https://www.tomsguide.com/computing/laptops/asus-zenbook-duo-2026-review">Asus Zenbook Duo</a> proved me dead wrong with its potent combination of two mesmerizing 14-inch OLED panels and the raw performance of Intel’s breakthrough Core Ultra X9 388H chipset.</p><p>It all starts with a ground-up ceraluminum redesign for the double display system that feels premium to the touch and sturdier against the elements. Throw in a CPU that has the power of a supercar paired with the efficiency of a Toyota Prius (you can actually game on this thing, and it has a 14-hour battery life), and it’s easily one of my favorite laptops of the year. — <em>Jason England, Managing Editor of Core Tech</em></p><h2 id="acer-predator-helios-18-ai">Acer Predator Helios 18 AI</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="eU59CembDesdgQWsb4Nvz6" name="Acer Predator Helios 18 AI" alt="Acer Predator Helios 18 AI" src="https://cdn.mos.cms.futurecdn.net/eU59CembDesdgQWsb4Nvz6-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>But what about in the gaming laptop space? There’s been a whole lot of monsters born this year, and Acer’s Predator Helios 18 AI definitely sits in that portable desktop replacement camp with what’s lying under the hood (up to an RTX 5090) and the massive wattage being pumped through to the chip.</p><p>Combine that with a gorgeously massive and buttery-smooth mini-LED panel, up to 192GB of RAM (in this economy!?) and a strong keyboard typing experience with plenty of ports, and you’ve got quite a winning formula. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 id="razer-blade-16-2026">Razer Blade 16 (2026)</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="es2oB6CoGAC4NyZCi9xtSJ" name="Razer Blade 16 (2026)" alt="Razer Blade 16 (2026)" src="https://cdn.mos.cms.futurecdn.net/es2oB6CoGAC4NyZCi9xtSJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/gaming-laptops/razer-blade-16-2026-review">Razer Blade 16 (2026)</a> is an ultraportable gaming laptop that makes no compromises. Thanks to its powerful components, it can run even the most demanding PC games at buttery-smooth frame rates. The vivid 16-inch OLED panel ensures all those games are a sight to behold.</p><p>The sleek design, with its sturdy, CNC-milled, all-black aluminum chassis, means this laptop will look just as great in the office as it is in your gaming den. Of course, RGB lighting on the keyboard deck shows the Blade 16 knows how to have fun. Though certainly pricey at $3,599 (to start), this is one of the finest gaming laptops I’ve reviewed and is worth your hard-earned cash.<em> — Tony Polanco, Senior Writer</em></p><h2 id="acer-chromebook-plus-spin-514">Acer Chromebook Plus Spin 514</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="tQ5vL2upFfZrpELPcFhtyJ" name="Acer Chromebook Plus Spin 514" alt="Acer Chromebook Plus Spin 514" src="https://cdn.mos.cms.futurecdn.net/tQ5vL2upFfZrpELPcFhtyJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>When MediaTek jumped into the Chromebook pool with its Kompanio Ultra ARM silicon, it was like Google’s laptops got a shot in the…well, arm. And of all the options released, the <a href="https://www.tomsguide.com/computing/chromebooks/acer-chromebook-plus-spin-514-review">Acer Chromebook Plus Spin 514</a> stands out as one of the most impressive.</p><p>Because it’s not just about the performance and power efficiency (almost 15 hours of battery life on one charge) or AI-infused simplicity of a Gemini-boosted Chrome OS. It’s about the experience you build around them, and this OLED 2-in-1 is one of the nicest-feeling Chromebooks I’ve touched. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 class="article-body__section" id="section-tablets"><span>Tablets</span></h2><h2 id="lenovo-tab-plus-gen-2">Lenovo Tab Plus Gen 2</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jboXFMxD4Wz4UV9FQF7UfJ" name="Lenovo Tab Plus Gen 2" alt="Lenovo Tab Plus Gen 2" src="https://cdn.mos.cms.futurecdn.net/jboXFMxD4Wz4UV9FQF7UfJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/android-tablets/the-gargantuan-speaker-on-this-android-tablet-isnt-a-gimmick-i-cant-believe-how-good-it-sounds">Lenovo Tab Plus Gen 2</a> is an entertainment-focused Android tablet that has the largest speaker I’ve ever seen. The JBL speaker is so large, you could even say it’s a speaker with a tablet attached, rather than the other way around. And it’s not for show either, as it delivers punchy, bassy sound you can feel in your chest. You can even use it as a Bluetooth speaker, which adds versatility.</p><p>The 12.1-inch IPS display is bright and colorful, making whatever you’re watching look its best. The 16:9 aspect ratio is great for videos, and it also works well for comic books and websites in vertical mode. There’s also a handy built-in kickstand when you want to sit back and relax at your desk. Though its performance isn’t the best for getting work done and it's too heavy to hold for long periods, the incredible sound and overall ease of use make this $399 tablet an easy recommendation.<em> — Tony Polanco, Senior Writer</em></p><h2 class="article-body__section" id="section-desktops"><span>Desktops</span></h2><h2 id="alienware-area-51-desktop">Alienware Area-51 Desktop</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Wb4EEwvgsSwqGQqgd3DgQJ" name="Alienware Area-51 Desktop" alt="Alienware Area-51 Desktop" src="https://cdn.mos.cms.futurecdn.net/Wb4EEwvgsSwqGQqgd3DgQJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/gaming-desktops/alienware-area-51-review">Alienware Area-51</a> is for hardcore users willing to pay top dollar for premium gaming performance. Aside from AMD Ryzen X3D processor options, this is effectively the same PC I reviewed last year. That’s not a bad thing, as this is still a phenomenal PC.</p><p>You can configure this beast with up to an Nvidia GeForce RTX 5090 GPU, 4TB of storage, and 64GB of RAM. You can also kit it out with an Intel Core Ultra 200S chip if you’re Team Blue. All of it is housed in an otherworldly design that keeps the PC running cool and quiet. If you have the space for it in your office or gaming den, this is the ultimate gaming PC.<em> — Tony Polanco, Senior Writer</em></p><h2 id="lenovo-yoga-mini">Lenovo Yoga Mini</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Xx4tn6EnLFYk6Tg3cCBo3K" name="Lenovo Yoga Mini i (1L, 11)" alt="Lenovo Yoga Mini i (1L, 11)" src="https://cdn.mos.cms.futurecdn.net/Xx4tn6EnLFYk6Tg3cCBo3K-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/desktop-computers/mini-pcs/lenovo-yoga-mini-review">Lenovo Yoga Mini</a> breaks free from the rectangular design of other mini PCs, adding cool extras like RGB underglow and touch controls. This circular mini PC is meant to sit front and center on your desk, and it looks absolutely stunning. </p><p>Despite its smaller size and unique shape, the Yoga Mini is equipped with Intel’s latest Ultra X5 and X7 chips to handle whatever you throw at it. Lenovo didn’t skimp on ports either, and you also get an integrated fingerprint scanner in the power button for secure and seamless logins. If you’re after a small yet unique mini PC that stands out from the crowd, this is one of the most impressive machines and designs I’ve seen yet. — <em>Anthony Spadafora, Managing Editor</em></p><h2 id="asus-rog-g1000">Asus ROG G1000</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="VCKwmG7G3htVPyChwrE8wJ" name="Asus ROG G1000" alt="Asus ROG G1000" src="https://cdn.mos.cms.futurecdn.net/VCKwmG7G3htVPyChwrE8wJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>So I’ll just say it upfront — Asus has created a monster that’s not just designed to be played, but to be seen as well. This is a monolithic pre-built tower that packs a whole bunch of flash courtesy of that holographic fan giving it a real stage presence atop your desk.</p><p>This is what happens when a company throws out the game plan and decides to do something a bit bonkers. Under the hood, you can spec it up to an AMD Ryzen 9 9950X3D2, an RTX 5090, a whopping 128GB of DDR5 RAM, and a 4TB SSD — complete with a 1,000-watt power supply keeping it all going. Top it off with some real cooling innovation with that Tri-zone airflow design, and this beast won’t break a sweat. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 id="khadas-mind-pro">Khadas Mind Pro</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:6589px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XHow7PCtKGK27essMZjZfU" name="Khadas Mind Pro Modular Mini PC" alt="Khadas Mind Pro Modular Mini PC" src="https://cdn.mos.cms.futurecdn.net/XHow7PCtKGK27essMZjZfU-1920-80.jpg" mos="" align="middle" fullscreen="" width="6589" height="3706" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Khadas)</span></figcaption></figure><p>Khadas continues to build on its modular mini PC ecosystem with its new Mind Pro. Although this portable mini PC packs in more power than ever before, it’s not that much larger than the original Mind or the Mind 2S. Likewise, thanks to Khadas’ proprietary Mind Connector on the bottom, it’s still compatible with the company’s existing docks, which instantly transform its mini PCs into a gaming rig or even a laptop.</p><p>The Mind Pro may be small, but like its predecessors, it still comes packed with full-sized ports, including dual Thunderbolt 4 ports. There’s also a 7.98Wh battery inside that lets this more powerful mini PC sleep for over 48 hours, so you can bring it from one workstation or location to another. The Mind Pro’s extra thickness over the Mind is certainly worth it if you want even more power in a similar form factor with a growing ecosystem of game-changing accessories. <em>— Anthony Spadafora, Managing Editor</em></p><h2 class="article-body__section" id="section-monitors"><span>Monitors</span></h2><h2 id="msi-mpg-341cqr-qd-oled-x36">MSI MPG 341CQR QD-OLED X36</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZoLiWyUquxJDjJUYGMscjJ" name="MSI MPG 341CQR QD-OLED X36" alt="MSI MPG 341CQR QD-OLED X36" src="https://cdn.mos.cms.futurecdn.net/ZoLiWyUquxJDjJUYGMscjJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>MSI knows how to make a great gaming monitor, and the <a href="https://www.tomsguide.com/computing/monitors/msi-mpg-341cqr-qd-oled-x36-review">MPG 341CQR QD-OLED X36</a> is one of its best so far — and certainly the best this year. A blistering 360Hz refresh rate and 0.03ms response time deliver buttery-smooth gameplay.</p><p>The monitor also boasts phenomenal color coverage and accuracy, making it ideal not just for gaming, but for professional color grading and editing too. The X36 also features an AI Care Sensor that automatically alters the monitor’s display settings by detecting a human in front of it — yes, AI isn’t just a gimmick on the X36. — <em>Nikita Achanta, Senior Reviews Writer</em></p><h2 id="alienware-27-240hz-qd-oled-gaming-monitor-aw2726dm">Alienware 27 240Hz QD-OLED Gaming Monitor (AW2726DM)</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="WJT4zMwdFkoMT3Lz7aZErJ" name="Alienware 27 240Hz QD-OLED Gaming Monitor (AW2726DM)" alt="Alienware 27 240Hz QD-OLED Gaming Monitor (AW2726DM)" src="https://cdn.mos.cms.futurecdn.net/WJT4zMwdFkoMT3Lz7aZErJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/monitors/alienware-aw2726dm-review">Alienware AW2726DM</a> is a premium gaming monitor that costs less than you’d expect. For $349, you get a gorgeous 27-inch 1440p OLED panel that makes everything look its best, a blazing-fast 240Hz refresh rate, and a low 0.03ms response time that can keep up with even the fastest-paced games.</p><p>The biggest compliment I can give the AW2726DM is that it has become my main monitor at work, proving that it’s not just good for gaming, as text on websites and documents appears nice and crisp. While HDR brightness could be brighter, and the design is very basic, lacking embellishments like RGB, this is a wonderful gaming monitor that won’t kill your bank account.<em> — Tony Polanco, Senior Writer</em></p><h2 id="samsung-32-inch-odyssey-g8-g80hs">Samsung 32-inch Odyssey G8 G80HS</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="VhhuEVwQAapx6RgAKNgGpJ" name="Samsung 32-inch Odyssey G8 G80HS" alt="Samsung 32-inch Odyssey G8 G80HS" src="https://cdn.mos.cms.futurecdn.net/VhhuEVwQAapx6RgAKNgGpJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/computing/monitors/i-checked-out-the-worlds-first-6k-gaming-monitor-and-its-a-sight-to-behold">Samsung Odyssey 32-inch G8 G80HS</a> is a marvel to behold thanks to its super-sharp 6K resolution, which is a first for gaming monitors, according to Samsung. It offers the most detailed picture quality I’ve ever seen. Trust me when I say that your eyes won’t be disappointed.</p><p>In addition to 6K at 165Hz, you can also switch to 3K mode at 330Hz for fast-paced competitive games. If you have a powerful PC that can play games at frame rates high enough to match that refresh rate, you’re in for a silky smooth and fast experience. While certainly pricey at its usual $1,599 asking price, you’re getting your money’s worth for visual fidelity and performance.<em> — Tony Polanco, Senior Writer</em></p><h2 class="article-body__section" id="section-audio"><span>Audio</span></h2><h2 id="edifier-hecate-g2000-pro">Edifier HECATE G2000 Pro</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="oorkXjCmYZiPZBBAanc6FJ" name="EDIFIER HECATE G2000 Pro" alt="EDIFIER HECATE G2000 Pro" src="https://cdn.mos.cms.futurecdn.net/oorkXjCmYZiPZBBAanc6FJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>Excellent sound quality? On a sub-$200 budget? The <a href="https://www.tomsguide.com/gaming/gaming-peripherals/edifier-g2000-pro-review">Edifier G2000 Pro</a> desktop speakers look straight out of a sci-fi movie, and they’ve got otherworldly sound quality to boot. The reactive RGB lighting dances to in-game sounds and the beat of drums when listening to music.</p><p>The G2000 Pro packs plenty of <em>oomph, </em>delivering deep, meaty bass that makes you feel immersed in RPGs, horror, and racing titles. The treble is bright and crisp, so you can hear the clashing of swords in excruciating detail — and it never becomes fatiguing. In-game dialogue is loud and clear, too, making the G2000 Pro one of the best computer speakers out there. — <em>Nikita Achanta, Senior Reviews Writer</em></p><h2 id="edifier-hecate-g5-max">Edifier HECATE G5 Max</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TApGFAiWFrmHqKR3aymjJJ" name="EDIFIER HECATE G5 Max" alt="EDIFIER HECATE G5 Max" src="https://cdn.mos.cms.futurecdn.net/TApGFAiWFrmHqKR3aymjJJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/gaming/gaming-peripherals/edifier-g5-max-review">Edifier G5 Max </a>makes you feel like the main character without breaking the bank. Equipped with a 53mm dynamic driver, 7.1.4 Spatial Audio co-developed by Hecate and THX, and an extremely comfortable design, the G5 Max also supports Hi-Res Audio streaming.</p><p>Chock-full of features usually reserved for premium prices, the G5 Max goes the extra mile by offering dual apps for customization. It is, by far, the best gaming headset for those on a budget. — <em>Nikita Achanta, Senior Reviews Writer</em></p><h2 id="sony-inzone-h6-air">Sony Inzone H6 Air</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rQ8g6XTnuitSmfLQbhAWgJ" name="Sony INZONE H6 Air" alt="Sony INZONE H6 Air" src="https://cdn.mos.cms.futurecdn.net/rQ8g6XTnuitSmfLQbhAWgJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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 <a href="https://www.tomsguide.com/gaming/gaming-peripherals/sony-inzone-h6-air-review">Sony Inzone H6 Air</a> proves that gaming headsets can be just as good for listening to music. Equipped with 40mm studio-grade drivers found in Sony’s monitoring headphones, the H6 Air is the ideal headset for audiophiles and gamers who want an immersive experience.</p><p>Whether you’re taking in the sights and sounds in open-world RPGs or infiltrating enemy bases in FPS titles, the H6 Air ensures you hear every soft footstep and loud explosion. Its open-back design offers superior comfort for all-day wear, and it comes with a detachable boom mic that makes you sound loud and clear. The H6 Air is the one headset to rule them all. — <em>Nikita Achanta, Senior Reviews Writer</em></p><h2 class="article-body__section" id="section-components"><span>Components</span></h2><h2 id="intel-core-ultra-7-270k-plus">Intel Core Ultra 7 270K Plus</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2Fd3Ep2gWWzy89oqbXRzoJ" name="Intel Core Ultra 7 270K" alt="Intel Core Ultra 7 270K Plus" src="https://cdn.mos.cms.futurecdn.net/2Fd3Ep2gWWzy89oqbXRzoJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>Intel’s been on a bit of a comeback tour this year, and the <a href="https://www.tomsguide.com/computing/cpus/intel-core-ultra-7-270k-plus-5-250k-plus-review">Core Ultra 7 270K Plus</a> is a highlight of its return to form. By focusing on the fundamentals and righting the wrongs of its previous “Arrow Lake” generation, Intel’s created something special (and well-priced) for your machine.</p><p>The productivity chops are still here — staying miles ahead of AMD in terms of getting stuff done. But with critical improvements to its gaming performance by speeding up the travel time for data inside the chip and offering performance packages, these vastly cheaper chips get uncomfortably close in potential to Team Red’s pricey X3D CPUs. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 id="amd-radeon-rx-9070-gre">AMD Radeon RX 9070 GRE</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Fj5U6sJkWJHWJWKksH8vfJ" name="AMD Radeon RX 9070 GRE" alt="AMD Radeon RX 9070 GRE" src="https://cdn.mos.cms.futurecdn.net/Fj5U6sJkWJHWJWKksH8vfJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>GPU pricing has become a bit of a running joke in 2026, but amid huge price increases, AMD has managed to hold down the fort, with the nicely powerful <a href="https://www.tomsguide.com/computing/gpus/amd-radeon-rx-9070-gre-review">Radeon RX 9070 GRE</a> staying (or even below) its MSRP.</p><p>Built atop AMD’s RDNA 4 architecture to support all that FSR Redstone goodness, this is a fantastic 1440p GPU that offers decent ray tracing performance, strong frame rates, and real value for money. It may not be the best for productivity, but if you want bang for your buck in gaming, this is solid. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 id="intel-arc-g3-extreme">Intel Arc G3 Extreme</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="wQR38ULkRaxbx9T2BaKgzJ" name="Intel Arc G3 Extreme inside MSI Claw 8 EX AI+" alt="Intel Arc G3 Extreme inside MSI Claw 8 EX AI+" src="https://cdn.mos.cms.futurecdn.net/wQR38ULkRaxbx9T2BaKgzJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>It’s a double win for Intel in the components category, as the Arc G3 Extreme in the <a href="https://www.tomsguide.com/gaming/handheld-gaming/msi-claw-8-ex-ai-plus-review">MSI Claw 8 EX AI+</a> delivers the next-generation breakthrough for gaming handhelds we’ve been waiting for.</p><p>Taking that monster integrated GPU from the Panther Lake laptop chips and stuffing it into a handheld (XeSS 3 multi-frame generation and all) has led to a giant leap not just in frames per second, but in power efficiency too. With just over four hours of battery life on one charge in our gaming testing, and the ability to play games on a 4K screen with ease, this is a phenomenal chip. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 class="article-body__section" id="section-accessories-and-peripherals"><span>Accessories and peripherals</span></h2><h2 id="tessan-voyager-205">Tessan Voyager 205</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hEhVhCtVv4mJhMBMdzo3gJ" name="Tessan Voyager 205" alt="Tessan Voyager 205" src="https://cdn.mos.cms.futurecdn.net/hEhVhCtVv4mJhMBMdzo3gJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>One thing that’s become an increasingly difficult task on my travels is keeping everything powered. The USB-C port demands can create a Frankenstein combination of travel adaptors all over my hotel room, but the <a href="https://www.tomsguide.com/computing/ive-traveled-50-000-miles-with-the-worlds-most-powerful-travel-adaptor-its-the-only-one-i-trust-to-charge-all-my-tech">Tessan Voyager 205</a> has the international compatibility and raw wattage to charge every single thing in my arsenal.</p><p>With three USB-C ports at 45W, three USB-C ports at 160W, USB-A, and a plug adaptor to plug something else in to make the most of that 205W output, this is a compact beast of a charger that keeps everything topped up with ease. <em>— Jason England, Managing Editor of Core Tech</em></p><h2 id="satechi-mac-mini-m4-stand-amp-hub-with-ssd-enclosure">Satechi Mac Mini M4 Stand & Hub with SSD Enclosure</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="s7JNNoDwdBeLbWFSCzokBJ" name="satechi Mac Mini M4 Stand & Hub" alt="satechi Mac Mini M4 Stand & Hub" src="https://cdn.mos.cms.futurecdn.net/s7JNNoDwdBeLbWFSCzokBJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>Satechi has long made some of the best Apple accessories, and its <a href="https://www.tomsguide.com/computing/peripherals/i-added-satechis-new-usb-c-hub-and-stand-to-my-mac-mini-m4-and-its-the-perfect-upgrade">Mac Mini M4 Stand & Hub</a> is the first accessory I’d pick up for Cupertino’s latest mini PC. Thanks to its anodized aluminum finish and dimensions, it complements the Mac mini M4 perfectly while giving you easy access to the device’s power button with a handy cutout on the back.</p><p>With Satechi’s Mac Mini M4 Stand & Hub, you can instantly add three USB-A ports and an SD card reader to your Mac, but that’s not all it can do. There’s also a free M.2 slot on the underside which lets you expand your Mac mini’s storage by up to 4TB. Whether you want to beat the Apple Tax on storage or add extra ports to your Mac mini M4, this accessory is easy to recommend. <em>— Anthony Spadafora, Managing Editor</em></p><h2 id="wobkey-rainy-75-pro">Wobkey Rainy 75 Pro</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YLGD2khjY4m2fGi3krZtYJ" name="Wobkey Rainy 75 Pro" alt="Wobkey Rainy 75 Pro" src="https://cdn.mos.cms.futurecdn.net/YLGD2khjY4m2fGi3krZtYJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" 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>Few keyboards are “designed for audiophiles,” but the <a href="https://www.tomsguide.com/computing/peripherals/wobkey-rainy-75-pro-review">Wobkey Rainy 75 Pro</a> is, and it’s the best-sounding mechanical keyboard I’ve ever tested. Its Kailh Cocoa switches, five layers of sound-dampening foam, and double-shot PBT keycaps come together to create a raindrop-like sound profile that’s gobsmackingly fantastic.</p><p>Plus, the Rainy 75 Pro is an absolute beast for productivity, thanks to its ultra-responsive switches and up to 900 hours of battery life. Its full CNC-machined aluminum construction also means the keyboard can stand the test of time. There really is no better deck for hardcore typists (and fans of a thocky sound). — <em>Nikita Achanta, Senior Reviews Writer</em></p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Tom's Guide TV Satisfaction Survey: Tell us what you love (and hate) about your TV ]]></title>
                                                                                                <dc:content><![CDATA[ <p>For the past few weeks we've been putting TV manufacturers' feet to the fire. We're holding them accountable by grading them in key categories like design, performance, features and pricing — and now we're asking our readers to get involved.</p><p>We recently asked readers <a href="https://www.tomsguide.com/tvs/we-asked-readers-which-tv-brand-they-trusted-most-and-one-company-rose-above-the-rest">which brand they trusted most and one company was the clear winner</a>. Now we're coming to you all directly with a short survey that will help us figure out what you love and hate about the six biggest TV makers: <a href="https://www.tomsguide.com/tvs/hisense-tv-report-card-2026-one-of-the-best-ways-to-maximize-your-dollar">Hisense</a>, <a href="https://www.tomsguide.com/tvs/lg-tvs-are-better-than-theyve-ever-been-heres-what-theyre-doing-right-what-theyre-doing-wrong-and-what-needs-to-change-in-2026">LG</a>, <a href="https://www.tomsguide.com/tvs/samsung-tv-report-card-2026-you-can-rely-on-samsung-to-deliver-exceptional-performance">Samsung</a>, <a href="https://www.tomsguide.com/tvs/theres-no-way-around-it-sony-tvs-are-priced-higher-than-most-of-the-competition-heres-what-sonys-doing-right-what-its-doing-wrong-and-what-needs-to-change-in-2026">Sony</a>, <a href="https://www.tomsguide.com/tvs/tcl-tv-report-card-2026-i-suspect-that-ive-recommended-tcl-tvs-more-than-any-other-brand">TCL</a> and <a href="https://www.tomsguide.com/tvs/vizio-tv-report-card-2026-vizio-is-still-finding-its-footing-post-walmart-acquisition">Vizio</a>.</p><p>Fill it out at your leisure and let me know in the comments what your biggest gripes are. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eG2mEX"></div>                            </div>                            <script src="https://kwizly.com/embed/eG2mEX.js" async></script><h2 id="here-s-how-i-d-answer">Here's how I'd answer</h2><p>Testing TVs for a living has left me with an embarrassing amount of screens in my home. In my home office I've got a 75-inch <a href="https://www.tomsguide.com/tvs/tcl-qm9k-mini-led-tv-review">TCL QM9K</a>. Downstairs in my living room is a <a href="https://www.tomsguide.com/tvs/oled-tvs/samsung-s95d-oled-review">Samsung S95D OLED</a>. In my music listening room I've got the <a href="https://www.tomsguide.com/tvs/hisense-u8n-mini-led-tv-review">Hisense U8N</a>. </p><p>In the above survey, I'd mark all three down.</p><p>How long have I owned those TVs? Not all that long. </p><p>The first TV I ever owned was a massive CRT model that I used to play N64 and PS1 games on growing up. My parents bought that TV for me and my brother in 1996 and I was still rocking it to play Halo 2 a decade later. </p><div><blockquote><p>These days, however, I'm trading out TVs every two to three years.</p></blockquote></div><p>These days, however, I'm trading out TVs every two to three years. That's probably a bit excessive, honestly, but as someone who covers this stuff, I want to make sure that I have access to all the latest and greatest technology to provide the best analysis to you guys. </p><p>Would I buy these TVs again? I absolutely would. They're all great value options. Not everyone probably feels the same as I feel, though. For some of you I'm sure the answer is going to be a resounding <em>no</em>. If that's you, drop a comment below letting me know why that is. </p><p>If the answer is <em>yes</em>, also drop a comment below letting me know why you like your TV so much. By doing so, you could be directly helping someone find their new favorite TV. </p><p>Stay tuned for the results of these surveys and check out our guide to the <a href="https://www.tomsguide.com/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up">best TV brands in 2026</a> if you haven't done so already. You'll be seeing the results used in more stories in the coming weeks as we continue to break down what's working — and what's not working — in the world of TVs.</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/tvs/theres-a-better-cable-for-tvs-than-hdmi-but-heres-why-well-probably-never-use-it">There's a better cable for TVs than HDMI — but here's why we'll probably never use it</a></li><li><a href="https://www.tomsguide.com/tvs/best-buy-essentials-ultra-thin-amplified-hdtv-antenna-review">Best Buy Essentials Ultra-Thin Amplified HDTV Antenna review</a></li><li><a href="https://www.tomsguide.com/tvs/your-streaming-stick-isnt-broken-these-apps-are-just-slowing-it-down-to-a-crawl">Your streaming stick isn't broken — these apps are just slowing it down to a crawl</a></li></ul><h2 id=""></h2> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/toms-guide-tv-satisfaction-survey-tell-us-what-you-love-and-hate-about-your-tv</link>
                                                                            <description>
                            <![CDATA[ Take our Tom's Guide TV Satisfaction Survey and tell us what you like about the TV you own and what you really don't like. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ZvFwSRtfH25kguR4zHU3hQ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/i2ni7uXmQ3KCaPGwekosSB-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 08 Aug 2026 07:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ Nick.Pino@futurenet.com (Nick Pino) ]]></author>                    <dc:creator><![CDATA[ Nick Pino ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/7xnnEdyK5eEbDVbS5pYB54-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nick Pino heads up the TV and AV verticals at Tom&#039;s Guide and covers everything from OLED TVs to the latest wireless headphones. He&#039;s a Level 1 Certified THX Home Theater Professional, and has attended classes on TV and audio calibration. On Tom&#039;s Guide, he looks after some of the site&#039;s important buying guides like the best TVs, best OLED TVs, best 4K TVs, so most of his day is spent watching and evaluating new screens from LG, Samsung, Sony, Hisense, TCL and Vizio. (And yes, he knows how lucky he is to say that.) He was formerly the Senior Editor, TV and AV at TechRadar (Tom&#039;s Guide&#039;s sister site) and has previously written for GamesRadar, Official Xbox Magazine, PC Gamer and other outlets over the last decade. He got his start on Best Buy&#039;s official video game magazine, @GAMER, for whom he attended his first E3 in 2013. He&#039;s served as a judge for CES&#039; prestigious Innovation Awards and has attended the tech conference in Las Vegas since 2014. He has a computer science degree from the University at Buffalo and still resides in the Buffalo area. (Go Bills!) Not sure which TV you should buy? Drop him an email or tweet him on Twitter and he can help you out.&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/i2ni7uXmQ3KCaPGwekosSB-1920-80.jpg">
                                                            <media:credit><![CDATA[Samsung, TCL, LG / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of Samsung, TCL and LG TVs in a room.]]></media:description>                                                            <media:text><![CDATA[A selection of Samsung, TCL and LG TVs in a room.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of Samsung, TCL and LG TVs in a room.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/i2ni7uXmQ3KCaPGwekosSB-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>For the past few weeks we've been putting TV manufacturers' feet to the fire. We're holding them accountable by grading them in key categories like design, performance, features and pricing — and now we're asking our readers to get involved.</p><p>We recently asked readers <a href="https://www.tomsguide.com/tvs/we-asked-readers-which-tv-brand-they-trusted-most-and-one-company-rose-above-the-rest">which brand they trusted most and one company was the clear winner</a>. Now we're coming to you all directly with a short survey that will help us figure out what you love and hate about the six biggest TV makers: <a href="https://www.tomsguide.com/tvs/hisense-tv-report-card-2026-one-of-the-best-ways-to-maximize-your-dollar">Hisense</a>, <a href="https://www.tomsguide.com/tvs/lg-tvs-are-better-than-theyve-ever-been-heres-what-theyre-doing-right-what-theyre-doing-wrong-and-what-needs-to-change-in-2026">LG</a>, <a href="https://www.tomsguide.com/tvs/samsung-tv-report-card-2026-you-can-rely-on-samsung-to-deliver-exceptional-performance">Samsung</a>, <a href="https://www.tomsguide.com/tvs/theres-no-way-around-it-sony-tvs-are-priced-higher-than-most-of-the-competition-heres-what-sonys-doing-right-what-its-doing-wrong-and-what-needs-to-change-in-2026">Sony</a>, <a href="https://www.tomsguide.com/tvs/tcl-tv-report-card-2026-i-suspect-that-ive-recommended-tcl-tvs-more-than-any-other-brand">TCL</a> and <a href="https://www.tomsguide.com/tvs/vizio-tv-report-card-2026-vizio-is-still-finding-its-footing-post-walmart-acquisition">Vizio</a>.</p><p>Fill it out at your leisure and let me know in the comments what your biggest gripes are. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eG2mEX"></div>                            </div>                            <script src="https://kwizly.com/embed/eG2mEX.js" async></script><h2 id="here-s-how-i-d-answer">Here's how I'd answer</h2><p>Testing TVs for a living has left me with an embarrassing amount of screens in my home. In my home office I've got a 75-inch <a href="https://www.tomsguide.com/tvs/tcl-qm9k-mini-led-tv-review">TCL QM9K</a>. Downstairs in my living room is a <a href="https://www.tomsguide.com/tvs/oled-tvs/samsung-s95d-oled-review">Samsung S95D OLED</a>. In my music listening room I've got the <a href="https://www.tomsguide.com/tvs/hisense-u8n-mini-led-tv-review">Hisense U8N</a>. </p><p>In the above survey, I'd mark all three down.</p><p>How long have I owned those TVs? Not all that long. </p><p>The first TV I ever owned was a massive CRT model that I used to play N64 and PS1 games on growing up. My parents bought that TV for me and my brother in 1996 and I was still rocking it to play Halo 2 a decade later. </p><div><blockquote><p>These days, however, I'm trading out TVs every two to three years.</p></blockquote></div><p>These days, however, I'm trading out TVs every two to three years. That's probably a bit excessive, honestly, but as someone who covers this stuff, I want to make sure that I have access to all the latest and greatest technology to provide the best analysis to you guys. </p><p>Would I buy these TVs again? I absolutely would. They're all great value options. Not everyone probably feels the same as I feel, though. For some of you I'm sure the answer is going to be a resounding <em>no</em>. If that's you, drop a comment below letting me know why that is. </p><p>If the answer is <em>yes</em>, also drop a comment below letting me know why you like your TV so much. By doing so, you could be directly helping someone find their new favorite TV. </p><p>Stay tuned for the results of these surveys and check out our guide to the <a href="https://www.tomsguide.com/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up">best TV brands in 2026</a> if you haven't done so already. You'll be seeing the results used in more stories in the coming weeks as we continue to break down what's working — and what's not working — in the world of TVs.</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/tvs/theres-a-better-cable-for-tvs-than-hdmi-but-heres-why-well-probably-never-use-it">There's a better cable for TVs than HDMI — but here's why we'll probably never use it</a></li><li><a href="https://www.tomsguide.com/tvs/best-buy-essentials-ultra-thin-amplified-hdtv-antenna-review">Best Buy Essentials Ultra-Thin Amplified HDTV Antenna review</a></li><li><a href="https://www.tomsguide.com/tvs/your-streaming-stick-isnt-broken-these-apps-are-just-slowing-it-down-to-a-crawl">Your streaming stick isn't broken — these apps are just slowing it down to a crawl</a></li></ul><h2 id=""></h2>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested Sony’s $3,600 Bravia 9 II True RGB TV — here's my verdict ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">Sony Bravia 9 II True RGB TV: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price: </strong>$3,599<br><strong>Screen size: </strong>65 inches<br><strong>Model: </strong>Sony K65XR90M2 <br><strong>Resolution: </strong>4K (3,840 x 2,160)<br><strong>HDR:</strong> Dolby Vision, HDR10, HLG<br><strong>Refresh rate: </strong>120Hz<br><strong>Ports: </strong>2x HDMI 2.1, 2x HDMI 2.0<br><strong>Smart TV software: </strong>Google TV<br><strong>Size (without stand)</strong>: 57 x 32.63 x 1.75 inches<br><strong>Weight (without stand): </strong>58.6 pounds</p></div></div><p>RGB backlight technology is clearly here to stay, and it’s laying the groundwork to eventually replace traditional Mini-LED as the de facto standard among the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html"><u>best TVs</u></a>. While more budget-friendly models like the <a href="https://www.tomsguide.com/tvs/qled-tvs/hisense-ur9-rgb-mini-led-tv-review"><u>Hisense UR9</u></a> are making the tech accessible, sets like the Sony Bravia 9 II represent the uncompromising high end, commanding a steep $3,599 starting price.</p><p>The seeds for this flagship were planted three years ago when <a href="https://www.tomsguide.com/news/i-saw-the-future-of-sonys-mini-led-tvs-and-it-looks-awesome"><u>I visited Sony’s Tokyo HQ to see early prototypes of its backlight drivers</u></a>. Sony’s mission then was granular control in service of "artistic intent.” The original <a href="https://www.tomsguide.com/tvs/sony-bravia-9-tv-review"><u>Sony Bravia 9</u></a> proved how potent that philosophy was, but the Bravia 9 II elevates it further by introducing a refined <a href="https://www.tomsguide.com/tvs/i-saw-sonys-true-rgb-up-close-and-the-downfall-of-oled-tvs-might-come-sooner-than-expected"><u>True RGB system</u></a>.</p><p>The result is a display that feels worth every penny. If OLEDs hadn't made massive brightness leaps of their own recently, I’d call this a full-blown OLED killer. A handful of other TVs might push higher raw peak nits on paper, but the Bravia 9 II pairs OLED-rivaling blacks, rich color performance, and an elite anti-reflective coating with a level of visual finesse that makes it the undisputed champion of RGB TVs. </p><h3 class="article-body__section" id="section-sony-bravia-9-ii-true-rgb-tv-price-and-sizes"><span>Sony Bravia 9 II True RGB TV: Price and sizes</span></h3><p>The Sony Bravia 9 II is positioned at the apex of Sony's current TV offerings, and its pricing reflects its ultra-flagship status:</p><ul><li>65-inch Sony Bravia 9 II (K65XR90M2):<a href="https://electronics.sony.com/tv-video/televisions/all-tvs/p/k65xr90m2"><u> $3,599.99</u></a></li><li>75-inch Sony Bravia 9 II (K75XR90M2):<a href="https://electronics.sony.com/tv-video/televisions/all-tvs/p/k75xr90m2"><u> $4,599.99</u></a></li><li>85-inch Sony Bravia 9 II (K85XR90M2):<a href="https://electronics.sony.com/tv-video/televisions/all-tvs/p/k85xr90m2"><u> $6,499.99</u></a></li><li>115-inch Sony Bravia 9 II (K115XR90M2): <a href="https://electronics.sony.com/tv-video/televisions/all-tvs/p/k115xr90m2"><u>$30,999</u></a> (not yet available)</li></ul><p>All sizes feature identical internal performance capabilities, though it's worth noting the gargantuan 115-inch model omits the Mirage stand found on its smaller siblings. And frankly, nobody needs to spend $30,000+ on an LED TV, no matter how good it is. </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.15%;"><img id="X5Ut8nuKDYv2NEJoGa8fcH" name="Sony-Bravia-9-II--15" alt="Pedestal stand on the Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/X5Ut8nuKDYv2NEJoGa8fcH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>While launch prices are steep, we have already seen modest discounts hit retailers, and you can expect more enticing sales around Black Friday. </p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-design-ports-and-connectivity"><span>Sony Bravia 9 II review: Design, ports and connectivity</span></h3><p>In terms of design, the highlight here is the new "Mirage" stand setup. When viewed from the right angle, the TV appears to float effortlessly above your console thanks to a magnetic lenticular cover plate that conceals cables fed cleanly down the center base. Much like the metallic floating bezel on Samsung's latest flagship <a href="https://www.tomsguide.com/tvs/oled-tvs/the-samsung-s95h-is-the-best-oled-tv-weve-tested-this-year-but-its-not-the-one-i-recommend-buying"><u>S95H OLED</u></a>, Sony's design choices make the TV look distinctive and elevated. On a macro-level, I’m excited to see TV manufacturers introducing memorable flourishes right now. </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.15%;"><img id="4HCP5GjBfUw8tPRNtELhjH" name="Sony-Bravia-9-II--13" alt="Rear view of the pedestal stand on the Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/4HCP5GjBfUw8tPRNtELhjH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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 if you’re after the slimmest TV, the Bravia 9 II isn’t it. Because it is a backlit Mini-LED system, the chassis is naturally thicker and heavier than paper-thin OLED alternatives (1.75 inches and 58.6 pounds, respectively, for the 65-inch model we reviewed). </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.15%;"><img id="UUg8yu4oS3jcHBqAqwZWaH" name="Sony-Bravia-9-II--12" alt="The side of a Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/UUg8yu4oS3jcHBqAqwZWaH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Bravia 9 II also sports an upgraded anti-reflective screen coating. In testing, it proved exceptionally capable at suppressing harsh indoor light sources and window reflections, easily rivaling Samsung's matte anti-glare solutions. Paired with the screen's immense light output (which I’ll detail further below,) this set thrives in sun-drenched rooms. </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.15%;"><img id="foH9pF79eyoeiNVKG28xiH" name="Sony-Bravia-9-II--16" alt="The ports on a Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/foH9pF79eyoeiNVKG28xiH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Where Sony stumbles is in its port selection: For a flagship in 2026, offering only two HDMI 2.1 ports (out of four total) remains a frustrating point of contention for multi-console gamers and AV enthusiasts who need multiple high-bandwidth connections. On the plus side, it does include an ATSC 3.0 tuner for broadcast 4K signals — something rival sets from Samsung and LG skimped out on in 2026.</p><h3 class="article-body__section" id="section-how-we-test-tvs"><span>How we test TVs</span></h3><p>We follow a standard testing protocol for every TV we review at Tom’s Guide. Our benchmarks include a series of technical and subject tests designed to rate the set’s performance.</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.15%;"><img id="eoSpoSzWvoFtQDo6BYBUiH" name="Sony-Bravia-9-II--4" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/eoSpoSzWvoFtQDo6BYBUiH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>For our technical tests, we use a Jeti spectraval 1501-HiRes spectroradiometer, a Klein K10-A colorimeter, a Murideo 8K-SIX-G Metal pattern generator, and <a href="https://www.portrait.com/calman-home/"><u>Portrait Displays’s Calman</u></a> calibration software to take measurements. We also use a Leo Bodnar 4K Input Lag Tester for determining whether it’s one of the <a href="https://www.tomsguide.com/tvs/qled-tvs/i-played-tons-of-games-on-tvs-this-year-heres-the-top-gaming-tvs-worth-buying-for-your-ps5-xbox-switch-and-more"><u>best gaming TVs</u></a>. For a more detailed look at what we do and how we do it, check out our “<a href="https://www.tomsguide.com/us/how-we-test-tvs,review-2478.html"><u>How we test TVs</u></a>” page. </p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-performance"><span>Sony Bravia 9 II review: Performance</span></h3><p>The Bravia 9 II’s picture performance impressed me most with its shadow precision and color vibrancy. Watching the rainy dock chase scene in “The Batman,” the colored lights piercing through the dark night sky looked completely controlled, intentional and hyper-vivid. Dark cityscape details remained intact without blowing out shadow details, while blooming around bright light sources was virtually non-existent.</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.15%;"><img id="X9pAvNW5YLJCi8UBimEkbH" name="Sony-Bravia-9-II--2" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/X9pAvNW5YLJCi8UBimEkbH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Color handling is equally outstanding. In “Dune: Part Two,” the vast desert sands and open skies of Arrakis displayed buttery-smooth gradations with zero visible color banding.</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.15%;"><img id="rATUexhJWwtZzm5UBQPLcH" name="Sony-Bravia-9-II--8" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/rATUexhJWwtZzm5UBQPLcH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I did notice some minor limitations in off-angle viewing, however. While Sony's wide-angle tech marks a huge improvement over the original Bravia 9, sitting far off-axis still introduces slight contrast drop-offs that you simply won't find on an OLED panel.</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.15%;"><img id="9cSbQAgbJWtympfHXHuVjH" name="Sony-Bravia-9-II--9" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/9cSbQAgbJWtympfHXHuVjH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Gaming on the Bravia 9 II feels fluid, and has natural benefits for PS5 owners. Swinging through New York in <a href="https://www.tomsguide.com/reviews/spider-man-2"><u>Marvel's Spider-Man 2</u></a> on PS5, motion remained crisp even during fast camera shifts. An input lag of 9.8 milliseconds is a monumental improvement over the Bravia 9’s 17 milliseconds, cracking the sub-10 millisecond threshold that’s pretty much the fastest we’ve seen on any Sony TV in the lab. </p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-test-results"><span>Sony Bravia 9 II review: Test results</span></h3><p>Speaking of our lab testing, the Sony Bravia 9 II delivered premium benchmark results, reflecting a massive generational step forward for Sony's backlighting. </p><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Sony Bravia 9 II</strong></p></td><td  ><p><strong>Sony Bravia 9</strong></p></td><td  ><p><strong>Sony Bravia 7 II</strong></p></td><td  ><p><strong>Hisense UR9</strong></p></td><td  ><p><strong>Samsung S95H OLED </strong></p></td></tr><tr><td class="firstcol " ><p><strong>SDR Brightness (10%, in nits)</strong></p></td><td  ><p>101</p></td><td  ><p>127</p></td><td  ><p>100</p></td><td  ><p>189</p></td><td  ><p>253</p></td></tr><tr><td class="firstcol " ><p><strong>Delta-E (lower is better)</strong></p></td><td  ><p>3.65</p></td><td  ><p>1.80</p></td><td  ><p>4.79</p></td><td  ><p>2.21</p></td><td  ><p>2.24</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 709 Gamut Coverage</strong></p></td><td  ><p>99.76%</p></td><td  ><p>99.17%</p></td><td  ><p>99.94%</p></td><td  ><p>99.90%</p></td><td  ><p>113.30%</p></td></tr><tr><td class="firstcol " ><p><strong>HDR Brightness (10%, in nits)</strong></p></td><td  ><p>3,903</p></td><td  ><p>2,713</p></td><td  ><p>2,077</p></td><td  ><p>4,370</p></td><td  ><p>2,721</p></td></tr><tr><td class="firstcol " ><p><strong>UHDA-P3 Gamut Coverage</strong></p></td><td  ><p>99.18%</p></td><td  ><p>95.32%</p></td><td  ><p>97.73%</p></td><td  ><p>99.65%</p></td><td  ><p>99.92%</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 2020 Gamut Coverage</strong></p></td><td  ><p>89.23%</p></td><td  ><p>78.02%</p></td><td  ><p>87.17%</p></td><td  ><p>93.14%</p></td><td  ><p>89.60%</p></td></tr><tr><td class="firstcol " ><p><strong>Input Latency (ms)</strong></p></td><td  ><p>9.8</p></td><td  ><p>17.0</p></td><td  ><p>10.4</p></td><td  ><p>12.1</p></td><td  ><p>9.7</p></td></tr></tbody></table></div><p>Out of the box, our production unit of the Sony Bravia 9 II reached 3,903 nits of peak HDR brightness in a 10% window (worth noting: a pre-production sample we tested previously managed 3,990 nits). </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.15%;"><img id="mMR257LE6NdkBVVRvNtXmH" name="Sony-Bravia-9-II--10" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/mMR257LE6NdkBVVRvNtXmH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>This represents a gigantic leap over the original Bravia 9's 2,713 nits. By shifting away from standard white LEDs to a True RGB backlight system, Sony proved it can drive immense luminance without blowing out color accuracy or clipping highlights. It comfortably outshines even the brightest flagship OLED we’ve tested (the Samsung S95H at 2,721 nits) while essentially matching its impressive 89.60% Rec. 2020 color gamut coverage. </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.15%;"><img id="J9CzqyKZqPEBXaiZeGc7fH" name="Sony-Bravia-9-II--6" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/J9CzqyKZqPEBXaiZeGc7fH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Hisense’s UR9 remains the raw brightness champion at 4,370 nits and holds a slight lead in Rec. 2020 gamut coverage (93.14% vs 89.23%), but almost no content is mastered to saturate BT.2020 fully. </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.15%;"><img id="eoSpoSzWvoFtQDo6BYBUiH" name="Sony-Bravia-9-II--4" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/eoSpoSzWvoFtQDo6BYBUiH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>When comparing the Bravia 9 II’s premium over Sony's step-down Bravia 7 II, the lab numbers tell an interesting story. The Bravia 7 II holds its own with a respectable 2,077 nits and nearly identical color coverage, but the flagship Bravia 9 II nearly doubles its peak HDR output and delivers superior out-of-the-box color accuracy. That would make a significant difference in a sun-drenched room. </p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-audio"><span>Sony Bravia 9 II review: Audio</span></h3><p>Sony re-architected the internal chassis of the Bravia 9 II to create more internal air volume, pairing its primary subwoofers with side-saddled tweeters and a newly added upward-firing beam tweeter. The result is an expansive soundstage with undeniable punch.</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.15%;"><img id="UerLNzaUZwjWDPQHjvDJeH" name="Sony-Bravia-9-II--11" alt="The back of a Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/UerLNzaUZwjWDPQHjvDJeH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Bravia 9 II also gets Sony’s Voice Zoom feature, which offers six levels of dialogue adjustment in either direction to adjust vocal clarity. I personally like using it for watching live sports when I want the commentary to pop clearly over roaring stadium crowds.</p><p>While I think it sounds great for a standalone TV, I also think a display of this caliber deserves a dedicated surround setup. If you stick within Sony's ecosystem, Acoustic Center Sync allows the TV’s built-in drivers to operate as the center channel when paired with a compatible Sony soundbar or home theater system. </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.15%;"><img id="JN72XkGA9FPzPrL5NviTeH" name="Sony-Bravia-9-II--14" alt="Pedestal stand on the Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/JN72XkGA9FPzPrL5NviTeH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Alongside this TV, Sony introduced the Bravia Theater Trio, a condensed central soundbar with wireless left and right channels. I haven’t tested it myself, but I’m intrigued by how it offers flexible placement options.</p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-software-and-interface"><span>Sony Bravia 9 II review: Software and interface</span></h3><p>The Bravia 9 II runs Google TV, which is one of my personal favorite smart TV platforms for daily use. You can take it or leave it, but Gemini AI integration now allows you to ask questions about movies, request quick briefs on sports scores, or catch up on daily news digests.</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.15%;"><img id="zciocALMxzVBMGE2GVw3dH" name="Sony-Bravia-9-II--5" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/zciocALMxzVBMGE2GVw3dH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Google TV Freeplay comes built-in, offering over 280 ad-supported channels for instant streaming. Users also receive free Sony Pictures Core credits (10 movie downloads plus 2 years of unlimited access to 100 curated Sony Pictures titles). Otherwise, app navigation for your subscribed services is generally fast and responsive, and it plays especially nice with YouTube TV for those like me who use it as their cable alternative.</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.15%;"><img id="JLbn9A9h4GYTk4T7KZUzbH" name="Sony-Bravia-9-II--7" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/JLbn9A9h4GYTk4T7KZUzbH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>When idle, the TV’s screensavers setting pulls from Google Photos, curated art galleries, or AI-generated art. Combined with the anti-reflective screen coating, the Bravia 9 II could satisfy that itch for an art TV aesthetic.</p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-remote"><span>Sony Bravia 9 II review: Remote</span></h3><p>Sony’s remote control is one of the best in the business. It features a comfortable, textured back and motion-sensitive backlighting that automatically illuminates the keys when you pick it up in a dark room and gently fades away once set back down. It’s fully rechargeable via USB-C.</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.15%;"><img id="pL8wT2pHPvEDBii6v5h8nH" name="Sony-Bravia-9-II--18" alt="The remote for the Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/pL8wT2pHPvEDBii6v5h8nH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Quick-launch shortcut keys are provided for Google TV Freeplay ("Free TV"), Sony Pictures Core, Crunchyroll, Netflix, Disney+, YouTube and Prime Video. There is also a customizable shortcut button that can be programmed to launch your most-used app or control Google Home smart devices. </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.15%;"><img id="pAFcNGLCUTwdmu7DcssvaH" name="Sony-Bravia-9-II--17" alt="The remote for the Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/pAFcNGLCUTwdmu7DcssvaH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I appreciate how it includes a dedicated Quick Settings toolbar button, too. It's a game-changer being able to tweak sleep timers or brightness levels without interrupting what you're watching.</p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-verdict"><span>Sony Bravia 9 II review: Verdict</span></h3><p>I’m convinced the Sony Bravia 9 II is the undisputed champion of RGB TVs. By pairing extreme peak luminance with unprecedented dimming control and top-tier processing, Sony has delivered a display that easily holds its ground against the <a href="https://www.tomsguide.com/tvs/oled-tvs/best-oled-tvs"><u>best OLED TVs</u></a> on the market. It isn't cheap, and the persistent limit of two HDMI 2.1 ports will annoy power users, but those are minor compromises on an otherwise breathtaking television. </p><p>If you want something cheaper (and we wouldn’t blame you for doing so), the step-down Sony Bravia 7 II delivers much of the same True RGB color magic for a significantly lower price tag. Alternatively, if you're still not convinced anything can beat OLED's perfect black levels, the LG C6 remains a phenomenal pick for dark-room viewing.</p><p>That said, if you have a bright living space and want the absolute peak of cinematic color and brightness finesse, the RGB LED-powered Bravia 9 II is worth every single penny.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-9-ii-true-rgb-tv-review</link>
                                                                            <description>
                            <![CDATA[ The Sony Bravia 9 II is a masterclass in Mini-LED, combining True RGB backlight technology with Sony’s refined picture processing. Delivering OLED-rivaling black levels, absurd HDR brightness and a thoroughly effective anti-reflective screen, it stands as the premier bright-room flagship for 2026. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">8ynXNnM8oakhHDaEWhzgVc</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/xNhd4ZWzGhMJFHBV2FCdLH-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 05 Aug 2026 19:45:10 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[4k TVs]]></category>
                                                    <category><![CDATA[TVs]]></category>
                                                                                                                    <dc:creator><![CDATA[ Kate Kozuch ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xAVUdx6Qtp3SzugnnfNYsL-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Kate Kozuch is a managing editor of social and video at Tom&#039;s Guide, where she&#039;s been with the team since 2019. She also reviews smartwatches, covers TVs, tests the latest audio products and dabbles in cooking appliances. Of course, that&#039;s not when she&#039;s working on building the &lt;a href=&quot;https://www.tomsguide.com/reference/smart-home-guide&quot;&gt;ultimate DIY smart home&lt;/a&gt;. She has conducted over 100 different product reviews across these categories, turning her findings into buying guides and face-offs. She also manages a number of gift guides on the site. &lt;/p&gt;&lt;p&gt;Kate has a strong on-camera presence as well. She has appeared on Cheddar and Fox 5 NY to talk trending tech news. She is also regularly featured on the Tom&#039;s Guide YouTube channel, runs the &lt;a href=&quot;https://www.tiktok.com/@tomsguide?lang=en&quot;&gt;Tom&#039;s Guide TikTok account&lt;/a&gt; with over 350,000 followers, and features all the tech she&#039;s testing &lt;a href=&quot;https://www.instagram.com/katekozuch/&quot;&gt;on her Instagram&lt;/a&gt;. When she’s not filming tech videos, you can find her taking up a new sport, mastering the NYT Crossword or channeling her inner celebrity chef. Speaking of, be sure to ask her about the time Guy Fieri made her a margarita at CES, or when her video of Martha Stewart drinking a margarita went mega-viral. Clearly, Kate has a thing for culinary icons and margaritas.&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/xNhd4ZWzGhMJFHBV2FCdLH-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sony Bravia 9 II TV on a desk]]></media:description>                                                            <media:text><![CDATA[Sony Bravia 9 II TV on a desk]]></media:text>
                                <media:title type="plain"><![CDATA[Sony Bravia 9 II TV on a desk]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/xNhd4ZWzGhMJFHBV2FCdLH-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">Sony Bravia 9 II True RGB TV: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price: </strong>$3,599<br><strong>Screen size: </strong>65 inches<br><strong>Model: </strong>Sony K65XR90M2 <br><strong>Resolution: </strong>4K (3,840 x 2,160)<br><strong>HDR:</strong> Dolby Vision, HDR10, HLG<br><strong>Refresh rate: </strong>120Hz<br><strong>Ports: </strong>2x HDMI 2.1, 2x HDMI 2.0<br><strong>Smart TV software: </strong>Google TV<br><strong>Size (without stand)</strong>: 57 x 32.63 x 1.75 inches<br><strong>Weight (without stand): </strong>58.6 pounds</p></div></div><p>RGB backlight technology is clearly here to stay, and it’s laying the groundwork to eventually replace traditional Mini-LED as the de facto standard among the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html"><u>best TVs</u></a>. While more budget-friendly models like the <a href="https://www.tomsguide.com/tvs/qled-tvs/hisense-ur9-rgb-mini-led-tv-review"><u>Hisense UR9</u></a> are making the tech accessible, sets like the Sony Bravia 9 II represent the uncompromising high end, commanding a steep $3,599 starting price.</p><p>The seeds for this flagship were planted three years ago when <a href="https://www.tomsguide.com/news/i-saw-the-future-of-sonys-mini-led-tvs-and-it-looks-awesome"><u>I visited Sony’s Tokyo HQ to see early prototypes of its backlight drivers</u></a>. Sony’s mission then was granular control in service of "artistic intent.” The original <a href="https://www.tomsguide.com/tvs/sony-bravia-9-tv-review"><u>Sony Bravia 9</u></a> proved how potent that philosophy was, but the Bravia 9 II elevates it further by introducing a refined <a href="https://www.tomsguide.com/tvs/i-saw-sonys-true-rgb-up-close-and-the-downfall-of-oled-tvs-might-come-sooner-than-expected"><u>True RGB system</u></a>.</p><p>The result is a display that feels worth every penny. If OLEDs hadn't made massive brightness leaps of their own recently, I’d call this a full-blown OLED killer. A handful of other TVs might push higher raw peak nits on paper, but the Bravia 9 II pairs OLED-rivaling blacks, rich color performance, and an elite anti-reflective coating with a level of visual finesse that makes it the undisputed champion of RGB TVs. </p><h3 class="article-body__section" id="section-sony-bravia-9-ii-true-rgb-tv-price-and-sizes"><span>Sony Bravia 9 II True RGB TV: Price and sizes</span></h3><p>The Sony Bravia 9 II is positioned at the apex of Sony's current TV offerings, and its pricing reflects its ultra-flagship status:</p><ul><li>65-inch Sony Bravia 9 II (K65XR90M2):<a href="https://electronics.sony.com/tv-video/televisions/all-tvs/p/k65xr90m2"><u> $3,599.99</u></a></li><li>75-inch Sony Bravia 9 II (K75XR90M2):<a href="https://electronics.sony.com/tv-video/televisions/all-tvs/p/k75xr90m2"><u> $4,599.99</u></a></li><li>85-inch Sony Bravia 9 II (K85XR90M2):<a href="https://electronics.sony.com/tv-video/televisions/all-tvs/p/k85xr90m2"><u> $6,499.99</u></a></li><li>115-inch Sony Bravia 9 II (K115XR90M2): <a href="https://electronics.sony.com/tv-video/televisions/all-tvs/p/k115xr90m2"><u>$30,999</u></a> (not yet available)</li></ul><p>All sizes feature identical internal performance capabilities, though it's worth noting the gargantuan 115-inch model omits the Mirage stand found on its smaller siblings. And frankly, nobody needs to spend $30,000+ on an LED TV, no matter how good it is. </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.15%;"><img id="X5Ut8nuKDYv2NEJoGa8fcH" name="Sony-Bravia-9-II--15" alt="Pedestal stand on the Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/X5Ut8nuKDYv2NEJoGa8fcH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>While launch prices are steep, we have already seen modest discounts hit retailers, and you can expect more enticing sales around Black Friday. </p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-design-ports-and-connectivity"><span>Sony Bravia 9 II review: Design, ports and connectivity</span></h3><p>In terms of design, the highlight here is the new "Mirage" stand setup. When viewed from the right angle, the TV appears to float effortlessly above your console thanks to a magnetic lenticular cover plate that conceals cables fed cleanly down the center base. Much like the metallic floating bezel on Samsung's latest flagship <a href="https://www.tomsguide.com/tvs/oled-tvs/the-samsung-s95h-is-the-best-oled-tv-weve-tested-this-year-but-its-not-the-one-i-recommend-buying"><u>S95H OLED</u></a>, Sony's design choices make the TV look distinctive and elevated. On a macro-level, I’m excited to see TV manufacturers introducing memorable flourishes right now. </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.15%;"><img id="4HCP5GjBfUw8tPRNtELhjH" name="Sony-Bravia-9-II--13" alt="Rear view of the pedestal stand on the Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/4HCP5GjBfUw8tPRNtELhjH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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 if you’re after the slimmest TV, the Bravia 9 II isn’t it. Because it is a backlit Mini-LED system, the chassis is naturally thicker and heavier than paper-thin OLED alternatives (1.75 inches and 58.6 pounds, respectively, for the 65-inch model we reviewed). </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.15%;"><img id="UUg8yu4oS3jcHBqAqwZWaH" name="Sony-Bravia-9-II--12" alt="The side of a Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/UUg8yu4oS3jcHBqAqwZWaH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Bravia 9 II also sports an upgraded anti-reflective screen coating. In testing, it proved exceptionally capable at suppressing harsh indoor light sources and window reflections, easily rivaling Samsung's matte anti-glare solutions. Paired with the screen's immense light output (which I’ll detail further below,) this set thrives in sun-drenched rooms. </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.15%;"><img id="foH9pF79eyoeiNVKG28xiH" name="Sony-Bravia-9-II--16" alt="The ports on a Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/foH9pF79eyoeiNVKG28xiH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Where Sony stumbles is in its port selection: For a flagship in 2026, offering only two HDMI 2.1 ports (out of four total) remains a frustrating point of contention for multi-console gamers and AV enthusiasts who need multiple high-bandwidth connections. On the plus side, it does include an ATSC 3.0 tuner for broadcast 4K signals — something rival sets from Samsung and LG skimped out on in 2026.</p><h3 class="article-body__section" id="section-how-we-test-tvs"><span>How we test TVs</span></h3><p>We follow a standard testing protocol for every TV we review at Tom’s Guide. Our benchmarks include a series of technical and subject tests designed to rate the set’s performance.</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.15%;"><img id="eoSpoSzWvoFtQDo6BYBUiH" name="Sony-Bravia-9-II--4" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/eoSpoSzWvoFtQDo6BYBUiH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>For our technical tests, we use a Jeti spectraval 1501-HiRes spectroradiometer, a Klein K10-A colorimeter, a Murideo 8K-SIX-G Metal pattern generator, and <a href="https://www.portrait.com/calman-home/"><u>Portrait Displays’s Calman</u></a> calibration software to take measurements. We also use a Leo Bodnar 4K Input Lag Tester for determining whether it’s one of the <a href="https://www.tomsguide.com/tvs/qled-tvs/i-played-tons-of-games-on-tvs-this-year-heres-the-top-gaming-tvs-worth-buying-for-your-ps5-xbox-switch-and-more"><u>best gaming TVs</u></a>. For a more detailed look at what we do and how we do it, check out our “<a href="https://www.tomsguide.com/us/how-we-test-tvs,review-2478.html"><u>How we test TVs</u></a>” page. </p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-performance"><span>Sony Bravia 9 II review: Performance</span></h3><p>The Bravia 9 II’s picture performance impressed me most with its shadow precision and color vibrancy. Watching the rainy dock chase scene in “The Batman,” the colored lights piercing through the dark night sky looked completely controlled, intentional and hyper-vivid. Dark cityscape details remained intact without blowing out shadow details, while blooming around bright light sources was virtually non-existent.</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.15%;"><img id="X9pAvNW5YLJCi8UBimEkbH" name="Sony-Bravia-9-II--2" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/X9pAvNW5YLJCi8UBimEkbH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Color handling is equally outstanding. In “Dune: Part Two,” the vast desert sands and open skies of Arrakis displayed buttery-smooth gradations with zero visible color banding.</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.15%;"><img id="rATUexhJWwtZzm5UBQPLcH" name="Sony-Bravia-9-II--8" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/rATUexhJWwtZzm5UBQPLcH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I did notice some minor limitations in off-angle viewing, however. While Sony's wide-angle tech marks a huge improvement over the original Bravia 9, sitting far off-axis still introduces slight contrast drop-offs that you simply won't find on an OLED panel.</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.15%;"><img id="9cSbQAgbJWtympfHXHuVjH" name="Sony-Bravia-9-II--9" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/9cSbQAgbJWtympfHXHuVjH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Gaming on the Bravia 9 II feels fluid, and has natural benefits for PS5 owners. Swinging through New York in <a href="https://www.tomsguide.com/reviews/spider-man-2"><u>Marvel's Spider-Man 2</u></a> on PS5, motion remained crisp even during fast camera shifts. An input lag of 9.8 milliseconds is a monumental improvement over the Bravia 9’s 17 milliseconds, cracking the sub-10 millisecond threshold that’s pretty much the fastest we’ve seen on any Sony TV in the lab. </p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-test-results"><span>Sony Bravia 9 II review: Test results</span></h3><p>Speaking of our lab testing, the Sony Bravia 9 II delivered premium benchmark results, reflecting a massive generational step forward for Sony's backlighting. </p><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Sony Bravia 9 II</strong></p></td><td  ><p><strong>Sony Bravia 9</strong></p></td><td  ><p><strong>Sony Bravia 7 II</strong></p></td><td  ><p><strong>Hisense UR9</strong></p></td><td  ><p><strong>Samsung S95H OLED </strong></p></td></tr><tr><td class="firstcol " ><p><strong>SDR Brightness (10%, in nits)</strong></p></td><td  ><p>101</p></td><td  ><p>127</p></td><td  ><p>100</p></td><td  ><p>189</p></td><td  ><p>253</p></td></tr><tr><td class="firstcol " ><p><strong>Delta-E (lower is better)</strong></p></td><td  ><p>3.65</p></td><td  ><p>1.80</p></td><td  ><p>4.79</p></td><td  ><p>2.21</p></td><td  ><p>2.24</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 709 Gamut Coverage</strong></p></td><td  ><p>99.76%</p></td><td  ><p>99.17%</p></td><td  ><p>99.94%</p></td><td  ><p>99.90%</p></td><td  ><p>113.30%</p></td></tr><tr><td class="firstcol " ><p><strong>HDR Brightness (10%, in nits)</strong></p></td><td  ><p>3,903</p></td><td  ><p>2,713</p></td><td  ><p>2,077</p></td><td  ><p>4,370</p></td><td  ><p>2,721</p></td></tr><tr><td class="firstcol " ><p><strong>UHDA-P3 Gamut Coverage</strong></p></td><td  ><p>99.18%</p></td><td  ><p>95.32%</p></td><td  ><p>97.73%</p></td><td  ><p>99.65%</p></td><td  ><p>99.92%</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 2020 Gamut Coverage</strong></p></td><td  ><p>89.23%</p></td><td  ><p>78.02%</p></td><td  ><p>87.17%</p></td><td  ><p>93.14%</p></td><td  ><p>89.60%</p></td></tr><tr><td class="firstcol " ><p><strong>Input Latency (ms)</strong></p></td><td  ><p>9.8</p></td><td  ><p>17.0</p></td><td  ><p>10.4</p></td><td  ><p>12.1</p></td><td  ><p>9.7</p></td></tr></tbody></table></div><p>Out of the box, our production unit of the Sony Bravia 9 II reached 3,903 nits of peak HDR brightness in a 10% window (worth noting: a pre-production sample we tested previously managed 3,990 nits). </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.15%;"><img id="mMR257LE6NdkBVVRvNtXmH" name="Sony-Bravia-9-II--10" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/mMR257LE6NdkBVVRvNtXmH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>This represents a gigantic leap over the original Bravia 9's 2,713 nits. By shifting away from standard white LEDs to a True RGB backlight system, Sony proved it can drive immense luminance without blowing out color accuracy or clipping highlights. It comfortably outshines even the brightest flagship OLED we’ve tested (the Samsung S95H at 2,721 nits) while essentially matching its impressive 89.60% Rec. 2020 color gamut coverage. </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.15%;"><img id="J9CzqyKZqPEBXaiZeGc7fH" name="Sony-Bravia-9-II--6" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/J9CzqyKZqPEBXaiZeGc7fH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Hisense’s UR9 remains the raw brightness champion at 4,370 nits and holds a slight lead in Rec. 2020 gamut coverage (93.14% vs 89.23%), but almost no content is mastered to saturate BT.2020 fully. </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.15%;"><img id="eoSpoSzWvoFtQDo6BYBUiH" name="Sony-Bravia-9-II--4" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/eoSpoSzWvoFtQDo6BYBUiH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>When comparing the Bravia 9 II’s premium over Sony's step-down Bravia 7 II, the lab numbers tell an interesting story. The Bravia 7 II holds its own with a respectable 2,077 nits and nearly identical color coverage, but the flagship Bravia 9 II nearly doubles its peak HDR output and delivers superior out-of-the-box color accuracy. That would make a significant difference in a sun-drenched room. </p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-audio"><span>Sony Bravia 9 II review: Audio</span></h3><p>Sony re-architected the internal chassis of the Bravia 9 II to create more internal air volume, pairing its primary subwoofers with side-saddled tweeters and a newly added upward-firing beam tweeter. The result is an expansive soundstage with undeniable punch.</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.15%;"><img id="UerLNzaUZwjWDPQHjvDJeH" name="Sony-Bravia-9-II--11" alt="The back of a Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/UerLNzaUZwjWDPQHjvDJeH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Bravia 9 II also gets Sony’s Voice Zoom feature, which offers six levels of dialogue adjustment in either direction to adjust vocal clarity. I personally like using it for watching live sports when I want the commentary to pop clearly over roaring stadium crowds.</p><p>While I think it sounds great for a standalone TV, I also think a display of this caliber deserves a dedicated surround setup. If you stick within Sony's ecosystem, Acoustic Center Sync allows the TV’s built-in drivers to operate as the center channel when paired with a compatible Sony soundbar or home theater system. </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.15%;"><img id="JN72XkGA9FPzPrL5NviTeH" name="Sony-Bravia-9-II--14" alt="Pedestal stand on the Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/JN72XkGA9FPzPrL5NviTeH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Alongside this TV, Sony introduced the Bravia Theater Trio, a condensed central soundbar with wireless left and right channels. I haven’t tested it myself, but I’m intrigued by how it offers flexible placement options.</p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-software-and-interface"><span>Sony Bravia 9 II review: Software and interface</span></h3><p>The Bravia 9 II runs Google TV, which is one of my personal favorite smart TV platforms for daily use. You can take it or leave it, but Gemini AI integration now allows you to ask questions about movies, request quick briefs on sports scores, or catch up on daily news digests.</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.15%;"><img id="zciocALMxzVBMGE2GVw3dH" name="Sony-Bravia-9-II--5" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/zciocALMxzVBMGE2GVw3dH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Google TV Freeplay comes built-in, offering over 280 ad-supported channels for instant streaming. Users also receive free Sony Pictures Core credits (10 movie downloads plus 2 years of unlimited access to 100 curated Sony Pictures titles). Otherwise, app navigation for your subscribed services is generally fast and responsive, and it plays especially nice with YouTube TV for those like me who use it as their cable alternative.</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.15%;"><img id="JLbn9A9h4GYTk4T7KZUzbH" name="Sony-Bravia-9-II--7" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/JLbn9A9h4GYTk4T7KZUzbH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>When idle, the TV’s screensavers setting pulls from Google Photos, curated art galleries, or AI-generated art. Combined with the anti-reflective screen coating, the Bravia 9 II could satisfy that itch for an art TV aesthetic.</p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-remote"><span>Sony Bravia 9 II review: Remote</span></h3><p>Sony’s remote control is one of the best in the business. It features a comfortable, textured back and motion-sensitive backlighting that automatically illuminates the keys when you pick it up in a dark room and gently fades away once set back down. It’s fully rechargeable via USB-C.</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.15%;"><img id="pL8wT2pHPvEDBii6v5h8nH" name="Sony-Bravia-9-II--18" alt="The remote for the Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/pL8wT2pHPvEDBii6v5h8nH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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>Quick-launch shortcut keys are provided for Google TV Freeplay ("Free TV"), Sony Pictures Core, Crunchyroll, Netflix, Disney+, YouTube and Prime Video. There is also a customizable shortcut button that can be programmed to launch your most-used app or control Google Home smart devices. </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.15%;"><img id="pAFcNGLCUTwdmu7DcssvaH" name="Sony-Bravia-9-II--17" alt="The remote for the Sony Bravia 9 II TV" src="https://cdn.mos.cms.futurecdn.net/pAFcNGLCUTwdmu7DcssvaH-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I appreciate how it includes a dedicated Quick Settings toolbar button, too. It's a game-changer being able to tweak sleep timers or brightness levels without interrupting what you're watching.</p><h3 class="article-body__section" id="section-sony-bravia-9-ii-review-verdict"><span>Sony Bravia 9 II review: Verdict</span></h3><p>I’m convinced the Sony Bravia 9 II is the undisputed champion of RGB TVs. By pairing extreme peak luminance with unprecedented dimming control and top-tier processing, Sony has delivered a display that easily holds its ground against the <a href="https://www.tomsguide.com/tvs/oled-tvs/best-oled-tvs"><u>best OLED TVs</u></a> on the market. It isn't cheap, and the persistent limit of two HDMI 2.1 ports will annoy power users, but those are minor compromises on an otherwise breathtaking television. </p><p>If you want something cheaper (and we wouldn’t blame you for doing so), the step-down Sony Bravia 7 II delivers much of the same True RGB color magic for a significantly lower price tag. Alternatively, if you're still not convinced anything can beat OLED's perfect black levels, the LG C6 remains a phenomenal pick for dark-room viewing.</p><p>That said, if you have a bright living space and want the absolute peak of cinematic color and brightness finesse, the RGB LED-powered Bravia 9 II is worth every single penny.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ We asked readers which TV brand they trusted most — and one company rose above the rest ]]></title>
                                                                                                <dc:content><![CDATA[ <p>As Senior TV Editor, I see nearly every TV release year after year. It’s a special opportunity to share a ton of helpful context with folks shopping for a new TV, but it also means that I’m liable to lose sight of how the average person feels about the state of the industry.</p><p>This is why, for the last several weeks, we’ve been asking you — the readers — which major TV brand you consider to be the most trustworthy.</p><p>Between Hisense, LG, Samsung, Sony, TCL and Vizio, one brand proved more trustworthy than the rest — and it wasn’t particularly close. Sony, makers of some of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> you can buy, rose above the competition. Here’s how the rest of the pack stacked up.</p><h2 id="trusted-tv-brands-full-results">Trusted TV brands: Full results</h2><p>In just over two weeks, our poll compiled just over 3,200 votes. Here's a breakdown of the results at the time of publishing:</p><div ><table><thead><tr><th class="firstcol " ><p>Brand</p></th><th  ><p>% Total</p></th><th  ><p># of Votes</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Sony</strong></p></td><td  ><p>32%</p></td><td  ><p>1,043</p></td></tr><tr><td class="firstcol " ><p><strong>LG</strong></p></td><td  ><p>25%</p></td><td  ><p>805</p></td></tr><tr><td class="firstcol " ><p><strong>Samsung</strong></p></td><td  ><p>24%</p></td><td  ><p>786</p></td></tr><tr><td class="firstcol " ><p><strong>TCL</strong></p></td><td  ><p>11%</p></td><td  ><p>352</p></td></tr><tr><td class="firstcol " ><p><strong>Hisense</strong></p></td><td  ><p>6%</p></td><td  ><p>201</p></td></tr><tr><td class="firstcol " ><p><strong>Vizio</strong></p></td><td  ><p>1%</p></td><td  ><p>46</p></td></tr></tbody></table></div><p>Among our readers, Sony-branded QLEDs, OLEDs and True RGB TVs are the most trusted. LG and Samsung are closely positioned in second and third place, while TCL, Hisense and Vizio received far fewer votes overall.</p><div  class="fancy-box"><div class="fancy_box-title">Want to learn more?</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kCA3oKqmzwMtQEzcP4kKWW" name="TG Brand Report Card FINAL LIST" caption="" alt="The final TV report card with every TV manufacturer" src="https://cdn.mos.cms.futurecdn.net/kCA3oKqmzwMtQEzcP4kKWW-1920-80.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text"><strong>Check out all of our report cards for the </strong><a data-analytics-id="inline-link" href="https://www.tomsguide.com/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up"><strong>best TV brands in 2026</strong></a><strong>.</strong></p></div></div><p>It's an interesting time to analyze these results. We recently conducted a week-long exercise in which we doled out report cards to these brands, grading each on performance, features, price and more. Sony received our highest grade for performance (A). However, Sony was also the recipient of the lowest grade we handed out for price (C-).</p><p>Our reasoning for these grades is simple: Sony's superb engineering and unparalleled picture processing amount to some of the best performance money can buy. Unfortunately, the cost of this consistency is often passed down to the viewer, as Sony TVs tend to be priced higher than rival TVs.</p><h2 id="sony-tvs-in-2026-and-beyond">Sony TVs in 2026 and beyond</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:3008px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LpifwEvPHLE3gisyuowv4d" name="Setting 2 BRAVIA family-FNL" alt="The Sony 2025 lineup of TVs and soundbars" src="https://cdn.mos.cms.futurecdn.net/LpifwEvPHLE3gisyuowv4d-1920-80.jpg" mos="" align="middle" fullscreen="" width="3008" height="1692" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>If Sony's 2026 Bravia TV lineup is any indication, the company is perfectly comfortable assuming the role of a trusted premium TV-maker — even if its mid-range and luxury models are priced higher than the competition.</p><p>Interestingly, our poll proves that viewers and TV-shoppers aren't necessarily fazed by this arrangement. Among our readers, Sony is more trustworthy than LG and Samsung (brands that specialize in high-end performance) and <em>far</em> more trustworthy than TCL and Hisense (brands commonly associated with value-forward, budget-friendly models).</p><p>Recently, Sony spun out its TV business, with rival brand TCL taking on a 51% stake in the operation. It's tough to say how this might affect Sony TVs, TCL TVs and the reputation of both brands.</p><p>We'll be continuing to run the poll, so if you haven't responded to our poll about TV-brand trustworthiness, we want to hear from you: Your input could help shape how these companies will build, tune, and market these TVs in 2027 and beyond. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.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/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up">We just graded every TV brand on price, performance and more — here’s how their report cards stack up</a></li><li><a href="https://www.tomsguide.com/tvs/4k-tvs/this-rgb-led-tv-is-the-one-to-buy-if-the-sony-bravia-9-ii-is-outside-of-your-budget">This RGB LED TV is the one to buy if the Sony Bravia 9 II is outside of your budget</a></li><li><a href="https://www.tomsguide.com/tvs/tcl-and-sony-are-joining-forces-to-sell-more-tvs-but-is-that-bad-news-for-shoppers">TCL and Sony are joining forces to sell more TVs — but is that bad news for shoppers?</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/we-asked-readers-which-tv-brand-they-trusted-most-and-one-company-rose-above-the-rest</link>
                                                                            <description>
                            <![CDATA[ Our poll asked readers which of the following TV brands have been the most reliable: Hisense, LG, Samsung, Sony, TCL and Vizio. Here's what you had to say. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">8Wc62Tm5kepmacBsABtjjK</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/8SELnGezPdAcTSxwoPW9YU-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 05 Aug 2026 05:15:00 +0000</pubDate>                                                                                                                                <updated>Tue, 11 Aug 2026 12:17:42 +0000</updated>
                                                                                                                                            <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ michael.desjardin@futurenet.com (Michael Desjardin) ]]></author>                    <dc:creator><![CDATA[ Michael Desjardin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/esytxRqW9GNVZNVCf7BFyV-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael Desjardin is a Senior Editor for TVs at Tom&#039;s Guide. He&#039;s been testing and tinkering with TVs professionally for over a decade, previously for Reviewed and USA Today. Michael studied media production and screenwriting at Emerson College. He loves cooking, zoning out to ambient music, and getting way too invested in the Red Sox. He considers himself living proof that TV doesn&#039;t necessarily rot your brain.&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/8SELnGezPdAcTSxwoPW9YU-1920-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A man thoughtfully looks at LCD TVs in a large store with his back to the camera]]></media:description>                                                            <media:text><![CDATA[A man thoughtfully looks at LCD TVs in a large store with his back to the camera]]></media:text>
                                <media:title type="plain"><![CDATA[A man thoughtfully looks at LCD TVs in a large store with his back to the camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/8SELnGezPdAcTSxwoPW9YU-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As Senior TV Editor, I see nearly every TV release year after year. It’s a special opportunity to share a ton of helpful context with folks shopping for a new TV, but it also means that I’m liable to lose sight of how the average person feels about the state of the industry.</p><p>This is why, for the last several weeks, we’ve been asking you — the readers — which major TV brand you consider to be the most trustworthy.</p><p>Between Hisense, LG, Samsung, Sony, TCL and Vizio, one brand proved more trustworthy than the rest — and it wasn’t particularly close. Sony, makers of some of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> you can buy, rose above the competition. Here’s how the rest of the pack stacked up.</p><h2 id="trusted-tv-brands-full-results">Trusted TV brands: Full results</h2><p>In just over two weeks, our poll compiled just over 3,200 votes. Here's a breakdown of the results at the time of publishing:</p><div ><table><thead><tr><th class="firstcol " ><p>Brand</p></th><th  ><p>% Total</p></th><th  ><p># of Votes</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Sony</strong></p></td><td  ><p>32%</p></td><td  ><p>1,043</p></td></tr><tr><td class="firstcol " ><p><strong>LG</strong></p></td><td  ><p>25%</p></td><td  ><p>805</p></td></tr><tr><td class="firstcol " ><p><strong>Samsung</strong></p></td><td  ><p>24%</p></td><td  ><p>786</p></td></tr><tr><td class="firstcol " ><p><strong>TCL</strong></p></td><td  ><p>11%</p></td><td  ><p>352</p></td></tr><tr><td class="firstcol " ><p><strong>Hisense</strong></p></td><td  ><p>6%</p></td><td  ><p>201</p></td></tr><tr><td class="firstcol " ><p><strong>Vizio</strong></p></td><td  ><p>1%</p></td><td  ><p>46</p></td></tr></tbody></table></div><p>Among our readers, Sony-branded QLEDs, OLEDs and True RGB TVs are the most trusted. LG and Samsung are closely positioned in second and third place, while TCL, Hisense and Vizio received far fewer votes overall.</p><div  class="fancy-box"><div class="fancy_box-title">Want to learn more?</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kCA3oKqmzwMtQEzcP4kKWW" name="TG Brand Report Card FINAL LIST" caption="" alt="The final TV report card with every TV manufacturer" src="https://cdn.mos.cms.futurecdn.net/kCA3oKqmzwMtQEzcP4kKWW-1920-80.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p class="fancy-box__body-text"><strong>Check out all of our report cards for the </strong><a data-analytics-id="inline-link" href="https://www.tomsguide.com/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up"><strong>best TV brands in 2026</strong></a><strong>.</strong></p></div></div><p>It's an interesting time to analyze these results. We recently conducted a week-long exercise in which we doled out report cards to these brands, grading each on performance, features, price and more. Sony received our highest grade for performance (A). However, Sony was also the recipient of the lowest grade we handed out for price (C-).</p><p>Our reasoning for these grades is simple: Sony's superb engineering and unparalleled picture processing amount to some of the best performance money can buy. Unfortunately, the cost of this consistency is often passed down to the viewer, as Sony TVs tend to be priced higher than rival TVs.</p><h2 id="sony-tvs-in-2026-and-beyond">Sony TVs in 2026 and beyond</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:3008px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LpifwEvPHLE3gisyuowv4d" name="Setting 2 BRAVIA family-FNL" alt="The Sony 2025 lineup of TVs and soundbars" src="https://cdn.mos.cms.futurecdn.net/LpifwEvPHLE3gisyuowv4d-1920-80.jpg" mos="" align="middle" fullscreen="" width="3008" height="1692" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>If Sony's 2026 Bravia TV lineup is any indication, the company is perfectly comfortable assuming the role of a trusted premium TV-maker — even if its mid-range and luxury models are priced higher than the competition.</p><p>Interestingly, our poll proves that viewers and TV-shoppers aren't necessarily fazed by this arrangement. Among our readers, Sony is more trustworthy than LG and Samsung (brands that specialize in high-end performance) and <em>far</em> more trustworthy than TCL and Hisense (brands commonly associated with value-forward, budget-friendly models).</p><p>Recently, Sony spun out its TV business, with rival brand TCL taking on a 51% stake in the operation. It's tough to say how this might affect Sony TVs, TCL TVs and the reputation of both brands.</p><p>We'll be continuing to run the poll, so if you haven't responded to our poll about TV-brand trustworthiness, we want to hear from you: Your input could help shape how these companies will build, tune, and market these TVs in 2027 and beyond. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.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/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up">We just graded every TV brand on price, performance and more — here’s how their report cards stack up</a></li><li><a href="https://www.tomsguide.com/tvs/4k-tvs/this-rgb-led-tv-is-the-one-to-buy-if-the-sony-bravia-9-ii-is-outside-of-your-budget">This RGB LED TV is the one to buy if the Sony Bravia 9 II is outside of your budget</a></li><li><a href="https://www.tomsguide.com/tvs/tcl-and-sony-are-joining-forces-to-sell-more-tvs-but-is-that-bad-news-for-shoppers">TCL and Sony are joining forces to sell more TVs — but is that bad news for shoppers?</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Sony is ditching discs as storage prices explode — here's how much it will cost you to download all 37 first-party games to your PS5 in 2026 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>This week, Sony held its Q3 earnings call where the company confirmed that it is "cautiously" moving forward with plans to stop producing physical versions of PlayStation games starting in 2028, CFO Lin Tao said (see Tao's comments in this <a href="https://x.com/ethangach/status/2083096283720356284" target="_blank">video from Kotaku's Ethan Gach</a>).</p><p>“When we think about the future, and we put in a lot of thought and time, and we cautiously considered this, and we came to this conclusion. And we’re going to cautiously move this forward,” Tao said.</p><p>Sony has been receiving near-daily backlash from gamers online; most of the backlash isn't over the medium of discs itself but a perceived lack of ownership since most digital sales are "licenses," not something you actually own. Data from <a href="https://bsky.app/profile/matpiscatella.bsky.social/post/3mrd2dzqyvk2l" target="_blank">Circana analyst Mat Piscatella</a> highlighted that in 2008 over 100 PlayStation games sold more than 100,000 copies. This year, only 7 reached that mark.</p><p>Unfortunately, Sony made this announcement just as prices are <a href="https://www.tomsguide.com/computing/hardware/sk-hynix-warns-2027-will-be-worst-year-for-ram-prices-right-as-a-damning-report-reveals-something-that-will-add-fuel-to-the-price-fixing-lawsuit">skyrocketing for RAM and storage</a>, forcing hikes on <a href="https://www.tomsguide.com/phones/iphones/we-reluctantly-raised-prices-tim-cook-weighs-in-on-hikes-as-apple-reports-usd109-billion-in-revenue">everything from phones to laptops</a> and <a href="https://www.tomsguide.com/entertainment/streaming-devices/some-streaming-sticks-are-doubling-in-price-heres-whats-driving-up-costs">anything that requires memory</a> thanks to AI-driven supply constraints.</p><p>With that in mind, I decided to take a look at how much memory you would need for the 37 first-party games released for the PlayStation 5 since it launched in 2020. </p><p>Here's the full list of the games</p><ul><li><strong>Astro Bot</strong></li><li><strong>Astro’s Playroom</strong></li><li><strong>Days Gone Remastered</strong></li><li><strong>Death Stranding: Director’s Cut</strong></li><li><strong>Death Stranding 2: On the Beach</strong></li><li><strong>Demon’s Souls remake</strong></li><li><strong>Ghost of Tsushima: Director’s Cut</strong></li><li><strong>Ghost of Yōtei</strong></li><li><strong>God of War Ragnarök</strong></li><li><strong>God of War: Sons of Sparta</strong></li><li><strong>Gran Turismo 7</strong></li><li><strong>Helldivers 2</strong></li><li><strong>Horizon Forbidden West</strong></li><li><strong>Horizon Zero Dawn Remastered</strong></li><li><strong>Lego Horizon Adventures</strong></li><li><strong>Lost Soul Aside</strong></li><li><strong>Midnight Murder Club</strong></li><li><strong>MLB The Show 21</strong></li><li><strong>MLB The Show 22</strong></li><li><strong>MLB The Show 23</strong></li><li><strong>MLB The Show 24</strong></li><li><strong>MLB The Show 25</strong></li><li><strong>MLB The Show 26</strong></li><li><strong>Nioh collection (Nioh 1 & 2 Remastered)</strong></li><li><strong>Ratchet & Clank: Rift Apart</strong></li><li><strong>Returnal</strong></li><li><strong>Rise of the Ronin</strong></li><li><strong>Sackboy: A Big Adventure</strong></li><li><strong>Saros</strong></li><li><strong>Spider-Man Remastered</strong></li><li><strong>Spider-Man 2</strong></li><li><strong>Spider-Man: Miles Morales</strong></li><li><strong>Stellar Blade</strong></li><li><strong>The Last of Us Part 1 remake</strong></li><li><strong>The Last of Us Part 2 Remastered</strong></li><li><strong>Uncharted: Legacy of Thieves Collection</strong></li><li><strong>Until Dawn remake</strong></li></ul><p>Using the listed download size from the PlayStation Store, I found that the 37 games totaled 2,989.1 GB, or about 2.99 TB. </p><p>The PS5 comes with 1TB of storage, which you can expand with an m.2 SSD or an external hard drive. To fit all of these games, plus whatever extra space is needed for the operating system and any patches you're going to want, at a minimum, a 4TB hard drive. </p><p>At the time of writing, the officially licensed 4TB WD Black NVMe m.2 SSD that PlayStation recommends<a href="https://www.amazon.com/Western-Digital-Officially-Licensed-Expansion/dp/B0C48HN4Z9/" target="_blank" rel="nofollow"> sells for $789.99 on Amazon</a>. That is $400 more than the same hard drive cost a year ago, and about as much as the PS5 Pro.</p><p>The cheapest 4TB drive, possibly compatible with the PS5, that I could find is a <a href="https://www.bestbuy.com/product/samsung-geek-squad-certified-refurbished-990-pro-4tb-internal-ssd-pcie-gen-4x4-nvme-with-heatsink-for-ps5/JCQ6HW393J" target="_blank" rel="nofollow">refurbished Samsung 990 Pro for $419.99 from Best Buy</a>.</p><p>Unfortunately, those prices aren't likely to get better, as SK Hynix, one of the big three RAM manufacturers alongside Samsung and Micron, recently <a href="https://www.tomsguide.com/computing/hardware/sk-hynix-warns-2027-will-be-worst-year-for-ram-prices-right-as-a-damning-report-reveals-something-that-will-add-fuel-to-the-price-fixing-lawsuit">warned that 2027 will be even worse</a> for RAM prices. And it's unlikely to get better by 2028, when Sony will stop making and selling physical copies of PS5 games.</p><p>Between rising prices and fears over ownership, gamers are stuck in a rough spot for the next few years. And that doesn't even account for the higher price they'll likely have to pay for a PS6 to play games from the next console generation.</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/tvs/theres-no-way-around-it-sony-tvs-are-priced-higher-than-most-of-the-competition-heres-what-sonys-doing-right-what-its-doing-wrong-and-what-needs-to-change-in-2026">Sony TV report card 2026: Is the 'Sony tax' still worth paying?</a></li><li><a href="https://www.tomsguide.com/gaming/playstation/if-playstation-wants-to-succeed-with-all-digital-it-needs-to-follow-the-steam-model-5-ways-it-can-win-over-gamers-like-me">If PlayStation wants to succeed with all-digital, it needs to follow the Steam model — 5 ways it can win over gamers like me</a></li><li><a href="https://www.tomsguide.com/tvs/theres-no-way-around-it-sony-tvs-are-priced-higher-than-most-of-the-competition-heres-what-sonys-doing-right-what-its-doing-wrong-and-what-needs-to-change-in-2026">Sony TV report card 2026: Is the 'Sony tax' still worth paying?</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/gaming/playstation/sony-is-ditching-discs-as-storage-prices-explode-heres-how-much-it-will-cost-you-to-download-all-37-first-party-games-to-your-ps5-in-2026</link>
                                                                            <description>
                            <![CDATA[ Here's how much storage you need to download every first-party PS5 game, and how much that large-capacity M.2 SSD will cost you. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">PgENZJRXYEEpU3HnAst2ZR</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/5hFD4w7688Ez8ofbMm7Kd5-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 01 Aug 2026 01:43:09 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Playstation]]></category>
                                                    <category><![CDATA[Gaming]]></category>
                                                                                                <author><![CDATA[ scott.younker@futurenet.com (Scott Younker) ]]></author>                    <dc:creator><![CDATA[ Scott Younker ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/RZsUpqcJ6Uj2q83oCUwNhQ-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Scott Younker is the West Coast Reporter at Tom’s Guide. He covers all the latest tech news, including phones, computing and more. He’s been involved in tech since 2011 covering everything from cameras and swimming pool equipment to the latest gaming consoles and smart TVs. He is on a seemingly never ending hunt to build the easiest to use home media system. &lt;/p&gt;&lt;p&gt;Before Tom’s Guide, Scott worked for publications like &lt;em&gt;Digital Trends, Outdoor Photographer, Dead Beats Panel&lt;/em&gt;, and in a brief detour, &lt;em&gt;America’s Funniest Home Videos&lt;/em&gt;. Yes, he has seen more pratfalls, silly pets and ridiculous home movie fails than is reasonably healthy. &lt;/p&gt;&lt;p&gt;When not writing about the latest devices or advances in chipsets, be sure to ask about Scott about disc golf and sustainability, or just if you’re being cheeky, ask about his noodle arm. If you truly want to get nerdy, bring up board games and his ongoing losing streak. &lt;/p&gt;&lt;p&gt;Scott joined Tom&#039;s Guide in 2024 as the West Coast Reporter. He graduated from the School of Journalism at the University of Oregon with a degree in magazine journalism and a minor in communications. While there he blogged or wrote for several magazines including the Fluxx, Ethos and the Oregon Commentator. He briefly wrote and managed a moderately successful blog focused on web comics. &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/5hFD4w7688Ez8ofbMm7Kd5-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide/Rory Mellon]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[PS5 hard drive and PS5 game cases]]></media:description>                                                            <media:text><![CDATA[PS5 hard drive and PS5 game cases]]></media:text>
                                <media:title type="plain"><![CDATA[PS5 hard drive and PS5 game cases]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/5hFD4w7688Ez8ofbMm7Kd5-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>This week, Sony held its Q3 earnings call where the company confirmed that it is "cautiously" moving forward with plans to stop producing physical versions of PlayStation games starting in 2028, CFO Lin Tao said (see Tao's comments in this <a href="https://x.com/ethangach/status/2083096283720356284" target="_blank">video from Kotaku's Ethan Gach</a>).</p><p>“When we think about the future, and we put in a lot of thought and time, and we cautiously considered this, and we came to this conclusion. And we’re going to cautiously move this forward,” Tao said.</p><p>Sony has been receiving near-daily backlash from gamers online; most of the backlash isn't over the medium of discs itself but a perceived lack of ownership since most digital sales are "licenses," not something you actually own. Data from <a href="https://bsky.app/profile/matpiscatella.bsky.social/post/3mrd2dzqyvk2l" target="_blank">Circana analyst Mat Piscatella</a> highlighted that in 2008 over 100 PlayStation games sold more than 100,000 copies. This year, only 7 reached that mark.</p><p>Unfortunately, Sony made this announcement just as prices are <a href="https://www.tomsguide.com/computing/hardware/sk-hynix-warns-2027-will-be-worst-year-for-ram-prices-right-as-a-damning-report-reveals-something-that-will-add-fuel-to-the-price-fixing-lawsuit">skyrocketing for RAM and storage</a>, forcing hikes on <a href="https://www.tomsguide.com/phones/iphones/we-reluctantly-raised-prices-tim-cook-weighs-in-on-hikes-as-apple-reports-usd109-billion-in-revenue">everything from phones to laptops</a> and <a href="https://www.tomsguide.com/entertainment/streaming-devices/some-streaming-sticks-are-doubling-in-price-heres-whats-driving-up-costs">anything that requires memory</a> thanks to AI-driven supply constraints.</p><p>With that in mind, I decided to take a look at how much memory you would need for the 37 first-party games released for the PlayStation 5 since it launched in 2020. </p><p>Here's the full list of the games</p><ul><li><strong>Astro Bot</strong></li><li><strong>Astro’s Playroom</strong></li><li><strong>Days Gone Remastered</strong></li><li><strong>Death Stranding: Director’s Cut</strong></li><li><strong>Death Stranding 2: On the Beach</strong></li><li><strong>Demon’s Souls remake</strong></li><li><strong>Ghost of Tsushima: Director’s Cut</strong></li><li><strong>Ghost of Yōtei</strong></li><li><strong>God of War Ragnarök</strong></li><li><strong>God of War: Sons of Sparta</strong></li><li><strong>Gran Turismo 7</strong></li><li><strong>Helldivers 2</strong></li><li><strong>Horizon Forbidden West</strong></li><li><strong>Horizon Zero Dawn Remastered</strong></li><li><strong>Lego Horizon Adventures</strong></li><li><strong>Lost Soul Aside</strong></li><li><strong>Midnight Murder Club</strong></li><li><strong>MLB The Show 21</strong></li><li><strong>MLB The Show 22</strong></li><li><strong>MLB The Show 23</strong></li><li><strong>MLB The Show 24</strong></li><li><strong>MLB The Show 25</strong></li><li><strong>MLB The Show 26</strong></li><li><strong>Nioh collection (Nioh 1 & 2 Remastered)</strong></li><li><strong>Ratchet & Clank: Rift Apart</strong></li><li><strong>Returnal</strong></li><li><strong>Rise of the Ronin</strong></li><li><strong>Sackboy: A Big Adventure</strong></li><li><strong>Saros</strong></li><li><strong>Spider-Man Remastered</strong></li><li><strong>Spider-Man 2</strong></li><li><strong>Spider-Man: Miles Morales</strong></li><li><strong>Stellar Blade</strong></li><li><strong>The Last of Us Part 1 remake</strong></li><li><strong>The Last of Us Part 2 Remastered</strong></li><li><strong>Uncharted: Legacy of Thieves Collection</strong></li><li><strong>Until Dawn remake</strong></li></ul><p>Using the listed download size from the PlayStation Store, I found that the 37 games totaled 2,989.1 GB, or about 2.99 TB. </p><p>The PS5 comes with 1TB of storage, which you can expand with an m.2 SSD or an external hard drive. To fit all of these games, plus whatever extra space is needed for the operating system and any patches you're going to want, at a minimum, a 4TB hard drive. </p><p>At the time of writing, the officially licensed 4TB WD Black NVMe m.2 SSD that PlayStation recommends<a href="https://www.amazon.com/Western-Digital-Officially-Licensed-Expansion/dp/B0C48HN4Z9/" target="_blank" rel="nofollow"> sells for $789.99 on Amazon</a>. That is $400 more than the same hard drive cost a year ago, and about as much as the PS5 Pro.</p><p>The cheapest 4TB drive, possibly compatible with the PS5, that I could find is a <a href="https://www.bestbuy.com/product/samsung-geek-squad-certified-refurbished-990-pro-4tb-internal-ssd-pcie-gen-4x4-nvme-with-heatsink-for-ps5/JCQ6HW393J" target="_blank" rel="nofollow">refurbished Samsung 990 Pro for $419.99 from Best Buy</a>.</p><p>Unfortunately, those prices aren't likely to get better, as SK Hynix, one of the big three RAM manufacturers alongside Samsung and Micron, recently <a href="https://www.tomsguide.com/computing/hardware/sk-hynix-warns-2027-will-be-worst-year-for-ram-prices-right-as-a-damning-report-reveals-something-that-will-add-fuel-to-the-price-fixing-lawsuit">warned that 2027 will be even worse</a> for RAM prices. And it's unlikely to get better by 2028, when Sony will stop making and selling physical copies of PS5 games.</p><p>Between rising prices and fears over ownership, gamers are stuck in a rough spot for the next few years. And that doesn't even account for the higher price they'll likely have to pay for a PS6 to play games from the next console generation.</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/tvs/theres-no-way-around-it-sony-tvs-are-priced-higher-than-most-of-the-competition-heres-what-sonys-doing-right-what-its-doing-wrong-and-what-needs-to-change-in-2026">Sony TV report card 2026: Is the 'Sony tax' still worth paying?</a></li><li><a href="https://www.tomsguide.com/gaming/playstation/if-playstation-wants-to-succeed-with-all-digital-it-needs-to-follow-the-steam-model-5-ways-it-can-win-over-gamers-like-me">If PlayStation wants to succeed with all-digital, it needs to follow the Steam model — 5 ways it can win over gamers like me</a></li><li><a href="https://www.tomsguide.com/tvs/theres-no-way-around-it-sony-tvs-are-priced-higher-than-most-of-the-competition-heres-what-sonys-doing-right-what-its-doing-wrong-and-what-needs-to-change-in-2026">Sony TV report card 2026: Is the 'Sony tax' still worth paying?</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Best TV Brands 2026: We just graded them on price, performance and more — here’s how their report cards stack up ]]></title>
                                                                                                <dc:content><![CDATA[ <p>This week, we handed out report cards to the biggest brands in the TV industry: <a href="https://www.tomsguide.com/tvs/hisense-tv-report-card-2026-one-of-the-best-ways-to-maximize-your-dollar">Hisense</a>, <a href="https://www.tomsguide.com/tvs/lg-tvs-are-better-than-theyve-ever-been-heres-what-theyre-doing-right-what-theyre-doing-wrong-and-what-needs-to-change-in-2026">LG</a>, <a href="https://www.tomsguide.com/tvs/samsung-tv-report-card-2026-you-can-rely-on-samsung-to-deliver-exceptional-performance">Samsung</a>, <a href="https://www.tomsguide.com/tvs/theres-no-way-around-it-sony-tvs-are-priced-higher-than-most-of-the-competition-heres-what-sonys-doing-right-what-its-doing-wrong-and-what-needs-to-change-in-2026">Sony</a>, <a href="https://www.tomsguide.com/tvs/tcl-tv-report-card-2026-i-suspect-that-ive-recommended-tcl-tvs-more-than-any-other-brand">TCL</a> and <a href="https://www.tomsguide.com/tvs/vizio-tv-report-card-2026-vizio-is-still-finding-its-footing-post-walmart-acquisition">Vizio</a>.</p><p>The goal was simple: grade each TV-maker on pricing, design, features and performance. We also took a hard look at each of these brands' software.</p><p>Now that these familiar names have received their respective report cards, it's time to meet them in the classroom for a conference about how they're doing this year. If you're shopping for the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TV</a> in 2026, this ought to give you a pretty clear picture about how to spend your money.</p><h2 id="pricing">Pricing</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="ArhUyWFK2seaATznAtyMmY" name="TV Brand Report Card_Final_PRICING" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/ArhUyWFK2seaATznAtyMmY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" 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>Of the six names in the mix, Hisense, TCL and Vizio led the class in the all-important pricing category. If you’ve shopped for a TV lately, this probably won’t come as a surprise.</p><p>Vizio has focused almost entirely on ultra-affordable TVs in recent years. While the rest of the big-name brands tinker with high-end, cutting-edge technologies like RGB LED and QD-OLED, Vizio is focused on more cost-effective hardware.</p><div><blockquote><p>When comparing similarly priced models, Hisense and TCL TVs offer a bit more bang for your buck when it comes to features.</p></blockquote></div><p>Meanwhile, the most-popular entry-level sets in Hisense and TCL’s primary lineups tend to be more affordable than those of LG, Samsung and Sony. In addition, it’s often the case that, when comparing similarly priced models, Hisense and TCL TVs offer a bit more bang for your buck when it comes to features.</p><p><strong>Winner: </strong>Tie (Hisense, TCL and Vizio)</p><h2 id="design">Design</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="2CZtBcUxff3jUmsu9tfrkY" name="TV Brand Report Card_Final_DESIGN" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/2CZtBcUxff3jUmsu9tfrkY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" 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>When it comes to design, Samsung and Sony lead the way. Sony’s top TVs often arrive with multiple stand configurations, and its most recent True RGB TVs feature a unique stand design that effectively hides cables by way of lenticular lensing.</p><p>Samsung consistently innovates at the higher end of its lineup, too, popularizing matte-style screens, external connectivity hubs and, most recently, eye-catching metallic frames. Affordable, entry-level Samsung QLEDs lean into the simplistic nature of their display hardware, often arriving with sleek, slim panels that play the part of a more-premium set.</p><div><blockquote><p>When it comes to design, Samsung and Sony lead the way.</p></blockquote></div><p>LG’s most noteworthy TVs — its OLEDs — are handsomely engineered, too, but the worldwide leader in OLED TVs doesn’t seem too eager to move away from tried and true formulas.</p><p>TCL and Hisense are purveyors of TVs with safe, middle-of-the-road designs that don’t innovate as much as they respond to (and replicate) industry trends. Their higher-end TVs tend to carry their best design characteristics.</p><p><strong>Winner: </strong>Tie (Samsung and Sony)</p><h2 id="features-2">Features</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="UUj45ydUmCH3eBk3uXqukY" name="TV Brand Report Card_Final_FEATURES" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/UUj45ydUmCH3eBk3uXqukY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" 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>Years ago, LG was among the first TV-makers to outfit its TVs with a full slate of HDMI 2.1 inputs. This aggressive approach to supplemental features has carried all the way into 2026, where the company narrowly leads TCL and Samsung when it comes to gaming- and streaming-related enhancements.</p><p>Although Samsung’s top-shelf TVs are jam-packed with extras, there are plenty of people shopping in this higher price tier expecting the full package. Without Dolby Vision support, LG’s top TVs are the most feature-complete.</p><div><blockquote><p>LG’s top TVs are the most feature-complete.</p></blockquote></div><p>Sony is the last of these brands to embrace full HDMI 2.1 support. Fortunately, that seems to be the new standard for the brand as the company looks to leverage newer chipsets in 2026 and beyond.</p><p>This year, Hisense and TCL are similar in how they approach feature availability across their respective lineups, but for the last couple of years, Hisense has traded a fourth HDMI input for a USB-C DisplayPort on its higher-end models — a move that might disappoint home theater enthusiasts looking to plug in every single one of their HDMI-equipped devices.</p><p>Being the brand most concerned with affordability above all else, it’s no surprise that Vizio is the brand least concerned with premium features.</p><p><strong>Winner: </strong>LG </p><h2 id="performance">Performance</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="p8hPGyPqCUWk2qTeAH5vmY" name="TV Brand Report Card_Final_PERFORMANCE" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/p8hPGyPqCUWk2qTeAH5vmY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Although TCL and Hisense make exceptional TVs, LG, Samsung and Sony are the brands to consider if you’re looking for top-tier, best-in-the-business performance.</p><p>Of the six brands we graded, LG, Samsung and Sony are the only three that make OLED TVs. Despite the rapid ascension of RGB LED technology, OLED remains the best all-around option (provided you don’t need super-high full-field brightness to overcome a sun-soaked room). And, of the graded brands releasing RGB LED TVs in 2026, Samsung and Sony have impressed me the most with the Samsung R95H and Sony Bravia 9 II.</p><p>These brands also deliver better picture processing than Hisense, TCL and Vizio. On the whole, Sony TVs feature better processing than any other brand, but LG and Samsung aren’t far behind.</p><div><blockquote><p>On the whole, Sony TVs feature better processing than any other brand.</p></blockquote></div><p>At the mid-range and entry level, ranking the brands’ performance isn’t as cut and dry. Hisense and TCL have fortified their position as the best bang-for-your-buck brands in these price tiers because they typically offer brighter pictures with better contrast control. In many cases, you could spend the same amount of money on an entry-level or mid-range LED TV from LG, Samsung or Sony and walk away with a less-impressive picture.</p><p>Vizio is… Well, <em>Vizio. </em>The Walmart-owned brand doesn’t make high-end flagships anymore, and the rest of its TVs are affordably made models engineered primarily to generate ad revenue by way of its web-based software. There’s nothing wrong with buying a Vizio TV as long as you’re shopping for affordability instead of sheer picture prowess.</p><p><strong>Winner: </strong>Sony</p><h2 id="smart-tv-platform">Smart TV platform</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="PGm7KsvsEjgrWCiwaizKnY" name="TV Brand Report Card_Final_SMART TV PLAT" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/PGm7KsvsEjgrWCiwaizKnY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" 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>Hisense, TCL and Sony offer the smoothest streaming experiences thanks to their built-in Google TV smart platform. It’s my second-favorite choice behind the Roku operating system on account of its relatively straightforward interface and vast library of downloadable apps.</p><p>LG and Samsung fall to a lower tier, primarily because their respective software suites aren’t as easy to navigate. Both LG’s webOS and Samsung’s Smart Hub also come with a unique set of annoyances, like unnecessary AI features and auto-playing content.</p><div><blockquote><p>Hisense, TCL and Sony offer the smoothest streaming experiences thanks to their built-in Google TV smart platform.</p></blockquote></div><p>Vizio is to be avoided if you’re hoping to avoid buying an external streaming device, as its proprietary software lags behind the five other brands. The interface is inundated with more ads than its competitors (including auto-playing commercials) and its app selection is limited.</p><p><strong>Winner: </strong>Hisense, TCL and Sony</p><h2 id="overall-brand-grades">Overall Brand Grades</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="bRS2YKg2xGTVmrfoo88fnY" name="TV Brand Report Card_Final_OVERALL GRADES" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/bRS2YKg2xGTVmrfoo88fnY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>So, you’re shopping for a TV this year and you want to put all of this information to proper use. Allow me to simplify things.</p><p>If you’re looking to splash out on a top-performing TV with an emphasis on design and features, LG and Samsung ought to be at the top of your list of options. At the highest price tier, Sony’s TVs offer similarly excellent performance and design, but some of these models don’t offer as many gaming-related features despite their steep cost.</p><p>If you’re looking for a mid-range TV in the <em>Goldilocks Zone </em>between affordability and high performance, consider Hisense and TCL. These two brands are especially suited for mid-range TV-shoppers on the hunt for the brightest possible picture, since Hisense and TCL specialize in budget-friendly Mini-LED TVs that don’t skimp on display hardware. (For what it’s worth, Hisense and TCL flagship models are usually quite good — just not as impressive as flagships from LG, Samsung and Sony.)</p><div><blockquote><p>Next year, we’ll grade all of these brands again. That means there’s plenty of time for them to study up.</p></blockquote></div><p>If you’re looking to spend as little as possible, I strongly recommend shopping for something from Hisense or TCL over anything from Insignia, Pioneer or Onn. I also recommend Hisense and TCL over Vizio, unless you’re comfortable with Vizio’s ad-heavy software experience.</p><p>Next year, we’ll grade all of these brands again. That means there’s plenty of time for them to study up.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.js" async></script> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/we-just-graded-every-tv-brand-on-price-performance-and-more-heres-how-their-report-cards-stack-up</link>
                                                                            <description>
                            <![CDATA[ Shopping for a TV in 2026? We just handed out report cards to Hisense, LG, Samsung, Sony, TCL and Vizio based on their TVs' price, performance and more. Here's how they grade. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">CeD98MzqPVY34FFfpiWURh</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/kCA3oKqmzwMtQEzcP4kKWW-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 31 Jul 2026 06:30:00 +0000</pubDate>                                                                                                                                <updated>Tue, 04 Aug 2026 11:36:41 +0000</updated>
                                                                                                                                            <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ michael.desjardin@futurenet.com (Michael Desjardin) ]]></author>                    <dc:creator><![CDATA[ Michael Desjardin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/esytxRqW9GNVZNVCf7BFyV-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael Desjardin is a Senior Editor for TVs at Tom&#039;s Guide. He&#039;s been testing and tinkering with TVs professionally for over a decade, previously for Reviewed and USA Today. Michael studied media production and screenwriting at Emerson College. He loves cooking, zoning out to ambient music, and getting way too invested in the Red Sox. He considers himself living proof that TV doesn&#039;t necessarily rot your brain.&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/kCA3oKqmzwMtQEzcP4kKWW-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The final TV report card with every TV manufacturer ]]></media:description>                                                            <media:text><![CDATA[The final TV report card with every TV manufacturer ]]></media:text>
                                <media:title type="plain"><![CDATA[The final TV report card with every TV manufacturer ]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/kCA3oKqmzwMtQEzcP4kKWW-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>This week, we handed out report cards to the biggest brands in the TV industry: <a href="https://www.tomsguide.com/tvs/hisense-tv-report-card-2026-one-of-the-best-ways-to-maximize-your-dollar">Hisense</a>, <a href="https://www.tomsguide.com/tvs/lg-tvs-are-better-than-theyve-ever-been-heres-what-theyre-doing-right-what-theyre-doing-wrong-and-what-needs-to-change-in-2026">LG</a>, <a href="https://www.tomsguide.com/tvs/samsung-tv-report-card-2026-you-can-rely-on-samsung-to-deliver-exceptional-performance">Samsung</a>, <a href="https://www.tomsguide.com/tvs/theres-no-way-around-it-sony-tvs-are-priced-higher-than-most-of-the-competition-heres-what-sonys-doing-right-what-its-doing-wrong-and-what-needs-to-change-in-2026">Sony</a>, <a href="https://www.tomsguide.com/tvs/tcl-tv-report-card-2026-i-suspect-that-ive-recommended-tcl-tvs-more-than-any-other-brand">TCL</a> and <a href="https://www.tomsguide.com/tvs/vizio-tv-report-card-2026-vizio-is-still-finding-its-footing-post-walmart-acquisition">Vizio</a>.</p><p>The goal was simple: grade each TV-maker on pricing, design, features and performance. We also took a hard look at each of these brands' software.</p><p>Now that these familiar names have received their respective report cards, it's time to meet them in the classroom for a conference about how they're doing this year. If you're shopping for the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TV</a> in 2026, this ought to give you a pretty clear picture about how to spend your money.</p><h2 id="pricing">Pricing</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="ArhUyWFK2seaATznAtyMmY" name="TV Brand Report Card_Final_PRICING" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/ArhUyWFK2seaATznAtyMmY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" 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>Of the six names in the mix, Hisense, TCL and Vizio led the class in the all-important pricing category. If you’ve shopped for a TV lately, this probably won’t come as a surprise.</p><p>Vizio has focused almost entirely on ultra-affordable TVs in recent years. While the rest of the big-name brands tinker with high-end, cutting-edge technologies like RGB LED and QD-OLED, Vizio is focused on more cost-effective hardware.</p><div><blockquote><p>When comparing similarly priced models, Hisense and TCL TVs offer a bit more bang for your buck when it comes to features.</p></blockquote></div><p>Meanwhile, the most-popular entry-level sets in Hisense and TCL’s primary lineups tend to be more affordable than those of LG, Samsung and Sony. In addition, it’s often the case that, when comparing similarly priced models, Hisense and TCL TVs offer a bit more bang for your buck when it comes to features.</p><p><strong>Winner: </strong>Tie (Hisense, TCL and Vizio)</p><h2 id="design">Design</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="2CZtBcUxff3jUmsu9tfrkY" name="TV Brand Report Card_Final_DESIGN" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/2CZtBcUxff3jUmsu9tfrkY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" 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>When it comes to design, Samsung and Sony lead the way. Sony’s top TVs often arrive with multiple stand configurations, and its most recent True RGB TVs feature a unique stand design that effectively hides cables by way of lenticular lensing.</p><p>Samsung consistently innovates at the higher end of its lineup, too, popularizing matte-style screens, external connectivity hubs and, most recently, eye-catching metallic frames. Affordable, entry-level Samsung QLEDs lean into the simplistic nature of their display hardware, often arriving with sleek, slim panels that play the part of a more-premium set.</p><div><blockquote><p>When it comes to design, Samsung and Sony lead the way.</p></blockquote></div><p>LG’s most noteworthy TVs — its OLEDs — are handsomely engineered, too, but the worldwide leader in OLED TVs doesn’t seem too eager to move away from tried and true formulas.</p><p>TCL and Hisense are purveyors of TVs with safe, middle-of-the-road designs that don’t innovate as much as they respond to (and replicate) industry trends. Their higher-end TVs tend to carry their best design characteristics.</p><p><strong>Winner: </strong>Tie (Samsung and Sony)</p><h2 id="features-2">Features</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="UUj45ydUmCH3eBk3uXqukY" name="TV Brand Report Card_Final_FEATURES" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/UUj45ydUmCH3eBk3uXqukY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" 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>Years ago, LG was among the first TV-makers to outfit its TVs with a full slate of HDMI 2.1 inputs. This aggressive approach to supplemental features has carried all the way into 2026, where the company narrowly leads TCL and Samsung when it comes to gaming- and streaming-related enhancements.</p><p>Although Samsung’s top-shelf TVs are jam-packed with extras, there are plenty of people shopping in this higher price tier expecting the full package. Without Dolby Vision support, LG’s top TVs are the most feature-complete.</p><div><blockquote><p>LG’s top TVs are the most feature-complete.</p></blockquote></div><p>Sony is the last of these brands to embrace full HDMI 2.1 support. Fortunately, that seems to be the new standard for the brand as the company looks to leverage newer chipsets in 2026 and beyond.</p><p>This year, Hisense and TCL are similar in how they approach feature availability across their respective lineups, but for the last couple of years, Hisense has traded a fourth HDMI input for a USB-C DisplayPort on its higher-end models — a move that might disappoint home theater enthusiasts looking to plug in every single one of their HDMI-equipped devices.</p><p>Being the brand most concerned with affordability above all else, it’s no surprise that Vizio is the brand least concerned with premium features.</p><p><strong>Winner: </strong>LG </p><h2 id="performance">Performance</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="p8hPGyPqCUWk2qTeAH5vmY" name="TV Brand Report Card_Final_PERFORMANCE" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/p8hPGyPqCUWk2qTeAH5vmY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Although TCL and Hisense make exceptional TVs, LG, Samsung and Sony are the brands to consider if you’re looking for top-tier, best-in-the-business performance.</p><p>Of the six brands we graded, LG, Samsung and Sony are the only three that make OLED TVs. Despite the rapid ascension of RGB LED technology, OLED remains the best all-around option (provided you don’t need super-high full-field brightness to overcome a sun-soaked room). And, of the graded brands releasing RGB LED TVs in 2026, Samsung and Sony have impressed me the most with the Samsung R95H and Sony Bravia 9 II.</p><p>These brands also deliver better picture processing than Hisense, TCL and Vizio. On the whole, Sony TVs feature better processing than any other brand, but LG and Samsung aren’t far behind.</p><div><blockquote><p>On the whole, Sony TVs feature better processing than any other brand.</p></blockquote></div><p>At the mid-range and entry level, ranking the brands’ performance isn’t as cut and dry. Hisense and TCL have fortified their position as the best bang-for-your-buck brands in these price tiers because they typically offer brighter pictures with better contrast control. In many cases, you could spend the same amount of money on an entry-level or mid-range LED TV from LG, Samsung or Sony and walk away with a less-impressive picture.</p><p>Vizio is… Well, <em>Vizio. </em>The Walmart-owned brand doesn’t make high-end flagships anymore, and the rest of its TVs are affordably made models engineered primarily to generate ad revenue by way of its web-based software. There’s nothing wrong with buying a Vizio TV as long as you’re shopping for affordability instead of sheer picture prowess.</p><p><strong>Winner: </strong>Sony</p><h2 id="smart-tv-platform">Smart TV platform</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="PGm7KsvsEjgrWCiwaizKnY" name="TV Brand Report Card_Final_SMART TV PLAT" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/PGm7KsvsEjgrWCiwaizKnY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" 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>Hisense, TCL and Sony offer the smoothest streaming experiences thanks to their built-in Google TV smart platform. It’s my second-favorite choice behind the Roku operating system on account of its relatively straightforward interface and vast library of downloadable apps.</p><p>LG and Samsung fall to a lower tier, primarily because their respective software suites aren’t as easy to navigate. Both LG’s webOS and Samsung’s Smart Hub also come with a unique set of annoyances, like unnecessary AI features and auto-playing content.</p><div><blockquote><p>Hisense, TCL and Sony offer the smoothest streaming experiences thanks to their built-in Google TV smart platform.</p></blockquote></div><p>Vizio is to be avoided if you’re hoping to avoid buying an external streaming device, as its proprietary software lags behind the five other brands. The interface is inundated with more ads than its competitors (including auto-playing commercials) and its app selection is limited.</p><p><strong>Winner: </strong>Hisense, TCL and Sony</p><h2 id="overall-brand-grades">Overall Brand Grades</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="bRS2YKg2xGTVmrfoo88fnY" name="TV Brand Report Card_Final_OVERALL GRADES" alt="The Tom's Guide 2026 Report Cards" src="https://cdn.mos.cms.futurecdn.net/bRS2YKg2xGTVmrfoo88fnY-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>So, you’re shopping for a TV this year and you want to put all of this information to proper use. Allow me to simplify things.</p><p>If you’re looking to splash out on a top-performing TV with an emphasis on design and features, LG and Samsung ought to be at the top of your list of options. At the highest price tier, Sony’s TVs offer similarly excellent performance and design, but some of these models don’t offer as many gaming-related features despite their steep cost.</p><p>If you’re looking for a mid-range TV in the <em>Goldilocks Zone </em>between affordability and high performance, consider Hisense and TCL. These two brands are especially suited for mid-range TV-shoppers on the hunt for the brightest possible picture, since Hisense and TCL specialize in budget-friendly Mini-LED TVs that don’t skimp on display hardware. (For what it’s worth, Hisense and TCL flagship models are usually quite good — just not as impressive as flagships from LG, Samsung and Sony.)</p><div><blockquote><p>Next year, we’ll grade all of these brands again. That means there’s plenty of time for them to study up.</p></blockquote></div><p>If you’re looking to spend as little as possible, I strongly recommend shopping for something from Hisense or TCL over anything from Insignia, Pioneer or Onn. I also recommend Hisense and TCL over Vizio, unless you’re comfortable with Vizio’s ad-heavy software experience.</p><p>Next year, we’ll grade all of these brands again. That means there’s plenty of time for them to study up.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.js" async></script>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Sony TV report card 2026: Is the 'Sony tax' still worth paying? ]]></title>
                                                                                                <dc:content><![CDATA[ <p>When buying a TV, you enter a long-term relationship with the brand that made it. For the next several years, your day-to-day viewing experience will be the result of countless decisions made by the folks who work for that company — from the TV’s performance to the feel of the software.</p><p>As professional TV-testers, no one understands this relationship better than us. That’s why we’re handing out report cards to the biggest names in the industry this week, grading each in categories ranging from price to performance and software. (Yesterday we evaluated <a href="https://www.tomsguide.com/tvs/lg-tvs-are-better-than-theyve-ever-been-heres-what-theyre-doing-right-what-theyre-doing-wrong-and-what-needs-to-change-in-2026">LG's 2026</a> so far.)</p><p>Today, we’re taking a look at Sony, makers of OLEDs, QLEDs and, most recently, a lineup of TVs that tap a brand-new technology that the company has dubbed <em>True RGB</em>.</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="JwyRDuj6qkGbKJd8S2NP5M" name="TV Brand Report Card 2026_SONY" alt="Tom's Guide's TV Report Card for Sony" src="https://cdn.mos.cms.futurecdn.net/JwyRDuj6qkGbKJd8S2NP5M-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" 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><h2 id="pricing-2">Pricing</h2><p>Let’s start with the most controversial aspect of Sony’s presence in the TV space. You might’ve heard of something called “the Sony Tax,” a derisive term used to describe the often high price of Sony TVs relative to rival models.</p><p>I try to avoid this term on account of its flippancy, but there’s no denying that the cost of any given Sony TV is typically higher than TVs in the same class with similar specs.</p><div><blockquote><p>There’s no denying that the cost of any given Sony TV is typically higher than TVs in the same class with similar specs.</p></blockquote></div><p>Take, for instance, the recently released <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Sony Bravia 7 II</a>. It’s the most affordable TV in Sony’s lineup to feature the brand’s True RGB display technology, but at the time of publishing, a 65-inch Bravia 7 II will set you back <a href="https://www.bestbuy.com/product/sony-65-class-bravia-7-ii-true-rgb-4k-hdr-google-tv-with-gemini/J7XSRH54W4" target="_blank" rel="nofollow">$1,999 at Best Buy</a>.</p><p>For an entry-level RGB LED TV, this is relatively steep. Consider this: Hisense’s most-affordable RGB LED TV, the UR8, is <a href="https://www.bestbuy.com/product/hisense-65-class-ur8-rgb-miniled-series-uhd-4k-hdr-smart-google-tv-2026/J3Z9Z42KXF" target="_blank" rel="nofollow">$1,299</a>. Even Samsung’s cheapest Micro RGB TV is <a href="https://www.bestbuy.com/product/samsung-65-class-r85h-series-micro-rgb-4k-uhd-samsungvisionai-smart-tizen-tv-2026/JJGRF3HY68" target="_blank" rel="nofollow">$1,699</a> at the 65-inch size point.</p><p>Some TV experts and A/V enthusiasts would argue that Sony’s premium pricing is justified by the brand’s meticulous engineering and commitment to consistency. In fact, I’m one of those people — there isn’t a brand I trust more when it comes to how well a TV handles <em>the little things</em>. Everything from upscaling to motion handling is almost guaranteed to be exceptionally realized on a Sony TV, and Bravia TVs in particular are considered by many to be guided by the best picture processing in the game.</p><p>But there’s no way around it: Sony TVs are priced higher than most of the competition.</p><p><em><strong>Grade: </strong></em><em>C-</em></p><h2 id="design-2">Design</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="5tKzi4PzJEW3rrfudB7Sof" name="Sony-Bravia-7-II--11" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/5tKzi4PzJEW3rrfudB7Sof-1920-80.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>Just as Sony TVs are known for excellent processing, so too have they earned a reputation for showcasing thoughtful design approaches.</p><p>Oftentimes, Sony TVs arrive with multiple stand configurations to accommodate different home theater setups. Mid-range and high-end Sony TVs ship with remote controls that offer a full spread of buttons (including an input button), and in recent years, these remotes are often backlit — a hugely convenient feature.</p><div><blockquote><p>The brand innovates in its design approach, too, which is not easy to do in an industry where standards are slow to change</p></blockquote></div><p>The brand innovates in its design approach, too, which is not easy to do in an industry where standards are slow to change. </p><p>Take the <a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs">Sony Bravia 9 II</a>, for instance (Sony’s top TV for 2026). This TV’s pedestal-style stand is outfitted with a special, lenticular material that hides cables when viewed from the front. </p><p>The illusion is tough to explain unless you’re standing in front of the Bravia 9 II, but it’s unlike any design flourish on the market from competing brands.</p><p><em><strong>Grade: </strong></em><em>A-</em></p><h2 id="features-3">Features</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="W3sBn7HXPCUfJ7jVg7F6pf" name="Sony-Bravia-7-II--16" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/W3sBn7HXPCUfJ7jVg7F6pf-1920-80.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>For the majority of my career covering the TV industry, Sony has been behind the eight ball somewhat when it comes to features. </p><p>On one hand, the brand isn’t afraid to showcase relatively niche enhancements for audio- and video-related functions, and high-end Sony TVs offer a <em>ton</em> of ways to tweak the sound and picture. But, conversely, Sony lagged behind its competitors in standardizing out-of-the-box features like Variable Refresh Rate (VRR), and it’s been slow to embrace the implementation of universal HDMI 2.1 inputs.</p><div><blockquote><p>Sony has been behind the eight ball somewhat when it comes to features</p></blockquote></div><p>The good news is that the brand seems to be in the process of course-correcting. The Bravia 3 II — an entry-level model for 2026 — comes with a full slate of HDMI 2.1 inputs. Weirdly, though, the Bravia 9 II is limited to just two out of four inputs with HDMI 2.1 functionality, due to the fact that it was developed over several years with an older chipset.</p><p><em><strong>Grade: </strong></em><em>B-</em></p><h2 id="performance-2">Performance</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="raocFFGLkEM6Zt2drWTVRC" name="Sony Bravia 8 II--1-LIST" alt="Sony Bravia 8 II on a TV stand" src="https://cdn.mos.cms.futurecdn.net/raocFFGLkEM6Zt2drWTVRC-1920-80.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>If you’re interested in an accurate presentation, nothing looks quite as sharp as a Sony TV. Its engineers sink countless hours into the development of new technologies designed to bring the picture as close as possible to the creator’s intent. Even at the lower end of the Bravia TV lineup, you can pretty much always count on Sony to make the most of a set’s hardware — whatever the limitations may be.</p><div><blockquote><p>If you’re interested in an accurate presentation, nothing looks quite as sharp as a Sony TV</p></blockquote></div><p>To give you an idea of Sony’s commitment to accuracy, consider this: For as searingly bright as the Sony Bravia 9 II is capable of getting, if you put a 10% white window on the screen and measure it in the TV’s Professional SDR picture mode, you will almost certainly receive a rating of 100 nits on the dot. This is because 100 nits is the industry standard for professional reference monitors when mastering for SDR.</p><p>Sony TVs may not be the brightest in any given year, but the attention to detail on display in everything from sub-4K upscaling to color gradients is staggeringly good. As a result, its TVs often lap brighter, punchier TVs in several key performance categories. </p><p>In other words, high-end Sony TVs are built for picture enthusiasts.</p><p><em><strong>Grade: </strong></em><em>A</em></p><h2 id="smart-tv-platform-2">Smart TV platform</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="FAg72YbFiogDCcq3a3DLmf" name="Sony-Bravia-7-II--17" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/FAg72YbFiogDCcq3a3DLmf-1920-80.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>Like several competing brands, Sony TVs leverage the Google TV streaming platform. While not perfect, for most people, this is perfectly fine.</p><p>The worst thing I can say about Google TV is that its layout is stuffed with sponsored content. This is more or less the case with every streaming platform outside of a few, though, and there’s a lot to be said for the sheer volume of downloadable services available via the platform’s app library.</p><div><blockquote><p>Aside from the claustrophobia-inducing amount of ads, using a Sony TV in 2026 is mostly a breeze</p></blockquote></div><p>For what it’s worth, I’ve always found that Sony’s own menu software harmonizes pretty well with the skeletal structure of Google TV. Aside from the claustrophobia-inducing amount of ads, using a Sony TV in 2026 is mostly a breeze.</p><p><em><strong>Grade: </strong></em><em>B+</em></p><h2 id="overall-grade-b">Overall grade: B-</h2><p>In 2026, Sony TVs are laser-targeted at home theater-owners, A/V enthusiasts and all-around picture purists. The attention to detail on display from a presentation standpoint is astonishing, and the brand's approach to design is among the best in the business, too.</p><p>Unfortunately, Sony TVs tend to be pricier than the competition. Even if you feel — as I do — that the higher pricing is often justified when considering the high level of performance associated with some of these sets, the fact of the matter is that it limits the scope of who can shop for certain Sony TVs.</p><p>If you're shopping for a Sony TV in 2026, the one to buy is the Sony Bravia 9 II. In a year chock-full of RGB LED TVs, Sony's flagship model is the gold standard. In many ways, given its unique design and intricate hardware, the Bravia 9 II is the "most Sony" TV that Sony has ever made.</p><div data-model-name="Sony Bravia 7 II,Sony Bravia 8 II QD-OLED TV,Sony Bravia 9 II (2026)" data-widget-type="multimodelreview" data-widget-title="Today's best 2026 Sony TV deals"></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.js" async></script> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/theres-no-way-around-it-sony-tvs-are-priced-higher-than-most-of-the-competition-heres-what-sonys-doing-right-what-its-doing-wrong-and-what-needs-to-change-in-2026</link>
                                                                            <description>
                            <![CDATA[ We're handing our report cards for every major TV brand on the price, performance and software of its TVs. Here's how Sony's QLEDs, OLEDs and True RGB TVs grade. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">RgoNRH9mVqmdLmdF82Q9zf</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/StvE9URPzbUbFj5bQC9qjC-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 27 Jul 2026 05:30:00 +0000</pubDate>                                                                                                                                <updated>Mon, 27 Jul 2026 11:41:05 +0000</updated>
                                                                                                                                            <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ michael.desjardin@futurenet.com (Michael Desjardin) ]]></author>                    <dc:creator><![CDATA[ Michael Desjardin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/esytxRqW9GNVZNVCf7BFyV-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael Desjardin is a Senior Editor for TVs at Tom&#039;s Guide. He&#039;s been testing and tinkering with TVs professionally for over a decade, previously for Reviewed and USA Today. Michael studied media production and screenwriting at Emerson College. He loves cooking, zoning out to ambient music, and getting way too invested in the Red Sox. He considers himself living proof that TV doesn&#039;t necessarily rot your brain.&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/StvE9URPzbUbFj5bQC9qjC-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A Sony Bravia 7 II RGB TV]]></media:description>                                                            <media:text><![CDATA[A Sony Bravia 7 II RGB TV]]></media:text>
                                <media:title type="plain"><![CDATA[A Sony Bravia 7 II RGB TV]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/StvE9URPzbUbFj5bQC9qjC-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>When buying a TV, you enter a long-term relationship with the brand that made it. For the next several years, your day-to-day viewing experience will be the result of countless decisions made by the folks who work for that company — from the TV’s performance to the feel of the software.</p><p>As professional TV-testers, no one understands this relationship better than us. That’s why we’re handing out report cards to the biggest names in the industry this week, grading each in categories ranging from price to performance and software. (Yesterday we evaluated <a href="https://www.tomsguide.com/tvs/lg-tvs-are-better-than-theyve-ever-been-heres-what-theyre-doing-right-what-theyre-doing-wrong-and-what-needs-to-change-in-2026">LG's 2026</a> so far.)</p><p>Today, we’re taking a look at Sony, makers of OLEDs, QLEDs and, most recently, a lineup of TVs that tap a brand-new technology that the company has dubbed <em>True RGB</em>.</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:4348px;"><p class="vanilla-image-block" style="padding-top:69.53%;"><img id="JwyRDuj6qkGbKJd8S2NP5M" name="TV Brand Report Card 2026_SONY" alt="Tom's Guide's TV Report Card for Sony" src="https://cdn.mos.cms.futurecdn.net/JwyRDuj6qkGbKJd8S2NP5M-1920-80.jpg" mos="" align="middle" fullscreen="" width="4348" height="3023" 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><h2 id="pricing-2">Pricing</h2><p>Let’s start with the most controversial aspect of Sony’s presence in the TV space. You might’ve heard of something called “the Sony Tax,” a derisive term used to describe the often high price of Sony TVs relative to rival models.</p><p>I try to avoid this term on account of its flippancy, but there’s no denying that the cost of any given Sony TV is typically higher than TVs in the same class with similar specs.</p><div><blockquote><p>There’s no denying that the cost of any given Sony TV is typically higher than TVs in the same class with similar specs.</p></blockquote></div><p>Take, for instance, the recently released <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Sony Bravia 7 II</a>. It’s the most affordable TV in Sony’s lineup to feature the brand’s True RGB display technology, but at the time of publishing, a 65-inch Bravia 7 II will set you back <a href="https://www.bestbuy.com/product/sony-65-class-bravia-7-ii-true-rgb-4k-hdr-google-tv-with-gemini/J7XSRH54W4" target="_blank" rel="nofollow">$1,999 at Best Buy</a>.</p><p>For an entry-level RGB LED TV, this is relatively steep. Consider this: Hisense’s most-affordable RGB LED TV, the UR8, is <a href="https://www.bestbuy.com/product/hisense-65-class-ur8-rgb-miniled-series-uhd-4k-hdr-smart-google-tv-2026/J3Z9Z42KXF" target="_blank" rel="nofollow">$1,299</a>. Even Samsung’s cheapest Micro RGB TV is <a href="https://www.bestbuy.com/product/samsung-65-class-r85h-series-micro-rgb-4k-uhd-samsungvisionai-smart-tizen-tv-2026/JJGRF3HY68" target="_blank" rel="nofollow">$1,699</a> at the 65-inch size point.</p><p>Some TV experts and A/V enthusiasts would argue that Sony’s premium pricing is justified by the brand’s meticulous engineering and commitment to consistency. In fact, I’m one of those people — there isn’t a brand I trust more when it comes to how well a TV handles <em>the little things</em>. Everything from upscaling to motion handling is almost guaranteed to be exceptionally realized on a Sony TV, and Bravia TVs in particular are considered by many to be guided by the best picture processing in the game.</p><p>But there’s no way around it: Sony TVs are priced higher than most of the competition.</p><p><em><strong>Grade: </strong></em><em>C-</em></p><h2 id="design-2">Design</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="5tKzi4PzJEW3rrfudB7Sof" name="Sony-Bravia-7-II--11" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/5tKzi4PzJEW3rrfudB7Sof-1920-80.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>Just as Sony TVs are known for excellent processing, so too have they earned a reputation for showcasing thoughtful design approaches.</p><p>Oftentimes, Sony TVs arrive with multiple stand configurations to accommodate different home theater setups. Mid-range and high-end Sony TVs ship with remote controls that offer a full spread of buttons (including an input button), and in recent years, these remotes are often backlit — a hugely convenient feature.</p><div><blockquote><p>The brand innovates in its design approach, too, which is not easy to do in an industry where standards are slow to change</p></blockquote></div><p>The brand innovates in its design approach, too, which is not easy to do in an industry where standards are slow to change. </p><p>Take the <a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs">Sony Bravia 9 II</a>, for instance (Sony’s top TV for 2026). This TV’s pedestal-style stand is outfitted with a special, lenticular material that hides cables when viewed from the front. </p><p>The illusion is tough to explain unless you’re standing in front of the Bravia 9 II, but it’s unlike any design flourish on the market from competing brands.</p><p><em><strong>Grade: </strong></em><em>A-</em></p><h2 id="features-3">Features</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="W3sBn7HXPCUfJ7jVg7F6pf" name="Sony-Bravia-7-II--16" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/W3sBn7HXPCUfJ7jVg7F6pf-1920-80.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>For the majority of my career covering the TV industry, Sony has been behind the eight ball somewhat when it comes to features. </p><p>On one hand, the brand isn’t afraid to showcase relatively niche enhancements for audio- and video-related functions, and high-end Sony TVs offer a <em>ton</em> of ways to tweak the sound and picture. But, conversely, Sony lagged behind its competitors in standardizing out-of-the-box features like Variable Refresh Rate (VRR), and it’s been slow to embrace the implementation of universal HDMI 2.1 inputs.</p><div><blockquote><p>Sony has been behind the eight ball somewhat when it comes to features</p></blockquote></div><p>The good news is that the brand seems to be in the process of course-correcting. The Bravia 3 II — an entry-level model for 2026 — comes with a full slate of HDMI 2.1 inputs. Weirdly, though, the Bravia 9 II is limited to just two out of four inputs with HDMI 2.1 functionality, due to the fact that it was developed over several years with an older chipset.</p><p><em><strong>Grade: </strong></em><em>B-</em></p><h2 id="performance-2">Performance</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="raocFFGLkEM6Zt2drWTVRC" name="Sony Bravia 8 II--1-LIST" alt="Sony Bravia 8 II on a TV stand" src="https://cdn.mos.cms.futurecdn.net/raocFFGLkEM6Zt2drWTVRC-1920-80.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>If you’re interested in an accurate presentation, nothing looks quite as sharp as a Sony TV. Its engineers sink countless hours into the development of new technologies designed to bring the picture as close as possible to the creator’s intent. Even at the lower end of the Bravia TV lineup, you can pretty much always count on Sony to make the most of a set’s hardware — whatever the limitations may be.</p><div><blockquote><p>If you’re interested in an accurate presentation, nothing looks quite as sharp as a Sony TV</p></blockquote></div><p>To give you an idea of Sony’s commitment to accuracy, consider this: For as searingly bright as the Sony Bravia 9 II is capable of getting, if you put a 10% white window on the screen and measure it in the TV’s Professional SDR picture mode, you will almost certainly receive a rating of 100 nits on the dot. This is because 100 nits is the industry standard for professional reference monitors when mastering for SDR.</p><p>Sony TVs may not be the brightest in any given year, but the attention to detail on display in everything from sub-4K upscaling to color gradients is staggeringly good. As a result, its TVs often lap brighter, punchier TVs in several key performance categories. </p><p>In other words, high-end Sony TVs are built for picture enthusiasts.</p><p><em><strong>Grade: </strong></em><em>A</em></p><h2 id="smart-tv-platform-2">Smart TV platform</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="FAg72YbFiogDCcq3a3DLmf" name="Sony-Bravia-7-II--17" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/FAg72YbFiogDCcq3a3DLmf-1920-80.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>Like several competing brands, Sony TVs leverage the Google TV streaming platform. While not perfect, for most people, this is perfectly fine.</p><p>The worst thing I can say about Google TV is that its layout is stuffed with sponsored content. This is more or less the case with every streaming platform outside of a few, though, and there’s a lot to be said for the sheer volume of downloadable services available via the platform’s app library.</p><div><blockquote><p>Aside from the claustrophobia-inducing amount of ads, using a Sony TV in 2026 is mostly a breeze</p></blockquote></div><p>For what it’s worth, I’ve always found that Sony’s own menu software harmonizes pretty well with the skeletal structure of Google TV. Aside from the claustrophobia-inducing amount of ads, using a Sony TV in 2026 is mostly a breeze.</p><p><em><strong>Grade: </strong></em><em>B+</em></p><h2 id="overall-grade-b">Overall grade: B-</h2><p>In 2026, Sony TVs are laser-targeted at home theater-owners, A/V enthusiasts and all-around picture purists. The attention to detail on display from a presentation standpoint is astonishing, and the brand's approach to design is among the best in the business, too.</p><p>Unfortunately, Sony TVs tend to be pricier than the competition. Even if you feel — as I do — that the higher pricing is often justified when considering the high level of performance associated with some of these sets, the fact of the matter is that it limits the scope of who can shop for certain Sony TVs.</p><p>If you're shopping for a Sony TV in 2026, the one to buy is the Sony Bravia 9 II. In a year chock-full of RGB LED TVs, Sony's flagship model is the gold standard. In many ways, given its unique design and intricate hardware, the Bravia 9 II is the "most Sony" TV that Sony has ever made.</p><div data-model-name="Sony Bravia 7 II,Sony Bravia 8 II QD-OLED TV,Sony Bravia 9 II (2026)" data-widget-type="multimodelreview" data-widget-title="Today's best 2026 Sony TV deals"></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.js" async></script>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ This RGB LED TV is the one to buy if the Sony Bravia 9 II is outside of your budget ]]></title>
                                                                                                <dc:content><![CDATA[ <p>We’re still evaluating the Bravia 9 II, but I’ll give you a little sneak-preview: Sony’s RGB LED flagship is one of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> of the year.</p><p>The brand’s spin on the all-new display technology — dubbed True RGB — serves as a reminder that Sony is one of the top TV-makers in the world. But if you were hoping to get your hands on the Bravia 9 II, you’ll have to make peace with its ultra-high price tag.</p><p>Fortunately, there’s another way to get a taste of Sony’s True RGB engineering at a much friendlier price: the <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Bravia 7 II</a>. Having tested the Bravia 7 II, we can confirm that it’s an impressive, mid-range alternative to its higher-end sibling. It's the set I recommend if you’re hesitant to splash out on the top TV in Sony’s lineup.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/X7i7NXCPPFY" allowfullscreen></iframe></div></div><h2 id="the-bravia-7-ii-is-the-mid-range-rgb-led-tv-to-beat-this-year">The Bravia 7 II is the mid-range RGB LED TV to beat this year</h2><p>As Sony’s most affordable True RGB TV, the Bravia 7 II may not be as impressive as the flagship model, but there’s an argument to be made that it’s better than the 2024 <a href="https://www.tomsguide.com/tvs/sony-bravia-9-tv-review">Bravia 9</a> (one of the best Mini-LED TVs I’ve ever seen).</p><p>Let’s take a look at how the Bravia 7 II stacks up against the original Bravia 9 and the all-new Bravia 9 II.</p><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Sony Bravia 7 II</strong></p></td><td  ><p><strong>Sony Bravia 9 II</strong></p></td><td  ><p><strong>Sony Bravia 9</strong></p></td></tr><tr><td class="firstcol " ><p>HDR Brightness (10%, in nits)</p></td><td  ><p>2,078</p></td><td  ><p>3,990</p></td><td  ><p>2,713</p></td></tr><tr><td class="firstcol " ><p>Rec. 2020 Gamut Coverage</p></td><td  ><p>87.2%</p></td><td  ><p>87.3%</p></td><td  ><p>78.02%</p></td></tr></tbody></table></div><p>As far as brightness goes, the Bravia 7 II peaks just below what we measured on the Bravia 9. This is plenty bright for most viewing environments, and while the Bravia 9 has some extra <em>oomph</em>, it’s not as significant as one might expect.</p><p>The real kicker is the Bravia 7 II’s ability to showcase color. Driven by Sony’s True RGB technology, it covers an impressive 87% of the BT.2020 color gamut. It’s a sizable step up from the Bravia 9 and right in line with the Bravia 9 II.</p><div class="product"><a data-dimension112="d10c2230-8539-11f1-ba4d-c199438f2c40" data-action="Deal Block" data-label="65" Bravia 7 II True RGB 4K TV" data-dimension48="65" Bravia 7 II True RGB 4K TV" data-dimension25="$1999" href="https://www.bestbuy.com/product/sony-65-class-bravia-7-ii-true-rgb-4k-hdr-google-tv-with-gemini/J7XSRH54W4" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="XAHA44ag88wbX7JJbYXA6T" name="Sony-Bravia-7-ii-True-RGB-TV-16-by-9-deal-block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/XAHA44ag88wbX7JJbYXA6T-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/sony-65-class-bravia-7-ii-true-rgb-4k-hdr-google-tv-with-gemini/J7XSRH54W4" target="_blank" rel="nofollow" data-dimension112="d10c2230-8539-11f1-ba4d-c199438f2c40" data-action="Deal Block" data-label='65" Bravia 7 II True RGB 4K TV' data-dimension48='65" Bravia 7 II True RGB 4K TV' data-dimension25="$1999">65" Bravia 7 II True RGB 4K TV: was $2599 now $1999</a></strong><br>Sony's mid-range True RGB TV blends cutting-edge display technology with Sony's superb picture processing. It also comes with a heaping helping of streaming- and -gaming-related features. Right now, the 65-inch version is on sale for $600 off at Best Buy.<a class="view-deal button" href="https://www.bestbuy.com/product/sony-65-class-bravia-7-ii-true-rgb-4k-hdr-google-tv-with-gemini/J7XSRH54W4" target="_blank" rel="nofollow" data-dimension112="d10c2230-8539-11f1-ba4d-c199438f2c40" data-action="Deal Block" data-label="65" Bravia 7 II True RGB 4K TV" data-dimension48="65" Bravia 7 II True RGB 4K TV" data-dimension25="$1999">View Deal</a></p></div><p>With a better array of RGB backlights with more-precise dimming, the Bravia 9 II is undoubtedly the better-performing TV. But make no mistake: The Bravia 7 II is packing a ton of horsepower under the hood.</p><p>Sony’s mid-range model also comes with a comparable set of features. Just like the Bravia 9 II, the 7 II supports 4K content at 120Hz across two of its four total HDMI inputs. You’re also getting Variable Refresh Rate (VRR), Dolby Vision support and the Google TV streaming platform right out of the box.</p><h2 id="sony-bravia-7-ii-outlook">Sony Bravia 7 II: outlook</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="5tPHvyuZ45bg5ZN2BKSRej" name="Sony-Bravia-7-II-True-RGB-TV" alt="Lifestyle image of Sony Bravia 7 II True RGB TV in living room environment" src="https://cdn.mos.cms.futurecdn.net/5tPHvyuZ45bg5ZN2BKSRej-1920-80.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: Sony)</span></figcaption></figure><div><blockquote><p>The Bravia 7 II feels closer in spirit to a high-end, luxury purchase than a middle-of-the road compromise.</p></blockquote></div><p>None of this is to suggest that the Bravia 9 II <em>isn’t</em> worth buying. Far from it. But as is the case with many mid-range OLED TVs (like the <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled-review">LG C6</a>), the Bravia 7 II feels closer in spirit to a high-end, luxury purchase than a middle-of-the road compromise.</p><p>Consider this: The <a href="https://www.bestbuy.com/product/sony-65-class-bravia-7-ii-true-rgb-4k-hdr-google-tv-with-gemini/J7XSRH54W4" target="_blank" rel="nofollow">65-inch Bravia 7 II just went on sale for $1,999 at Best Buy</a>. That’s $600 off its original price and only a couple hundred bucks more than the 65-inch C6 OLED.</p><p>The value proposition is even more compelling when you consider the top-shelf option. Right now, the <a href="https://www.bestbuy.com/product/sony-65-class-bravia-9-ii-true-rgb-4k-hdr-google-tv-with-gemini-and-anti-glare-screen/J7XSRH54TJ/sku/6674085" target="_blank" rel="nofollow">65-inch Bravia 9 II is $3,299 at Best Buy</a> — a whopping $1,300 more than the Bravia 7 II.</p><p>The Bravia 9 II might prove to be one of the best-performing TVs of the year, but the Bravia 7 II will almost certainly be the set I recommend more often between now and the holiday-shopping season. This True RGB has a chance to be a true sleeper hit.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.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/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Sony’s new Bravia 7 II has a secret weapon that makes it so good</a></li><li><a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs">I saw Sony's Bravia 9 II up close and it completely shattered my expectations — here’s why it’s the king of RGB TVs</a></li><li><a href="https://www.tomsguide.com/tvs/theres-a-better-cable-for-tvs-than-hdmi-but-heres-why-well-probably-never-use-it">There's a better cable for TVs than HDMI — but here's why we'll probably never use it</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/4k-tvs/this-rgb-led-tv-is-the-one-to-buy-if-the-sony-bravia-9-ii-is-outside-of-your-budget</link>
                                                                            <description>
                            <![CDATA[ The Sony Bravia 9 II may be one of the best TVs of 2026, but the brand's mid-range True RGB TV (the Bravia 7 II) is a better buy for most people. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">jqt4xNvq97qp4vCNt7Rojc</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/WcUnydfJ49spUBjEFbazE4-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 22 Jul 2026 11:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[4k TVs]]></category>
                                                    <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ michael.desjardin@futurenet.com (Michael Desjardin) ]]></author>                    <dc:creator><![CDATA[ Michael Desjardin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/esytxRqW9GNVZNVCf7BFyV-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael Desjardin is a Senior Editor for TVs at Tom&#039;s Guide. He&#039;s been testing and tinkering with TVs professionally for over a decade, previously for Reviewed and USA Today. Michael studied media production and screenwriting at Emerson College. He loves cooking, zoning out to ambient music, and getting way too invested in the Red Sox. He considers himself living proof that TV doesn&#039;t necessarily rot your brain.&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/WcUnydfJ49spUBjEFbazE4-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Sony Bravia 7 II displaying colorful, cinematic content alongside a super-imposed badge that reads: &quot;TG Flex for Less&quot;]]></media:description>                                                            <media:text><![CDATA[The Sony Bravia 7 II displaying colorful, cinematic content alongside a super-imposed badge that reads: &quot;TG Flex for Less&quot;]]></media:text>
                                <media:title type="plain"><![CDATA[The Sony Bravia 7 II displaying colorful, cinematic content alongside a super-imposed badge that reads: &quot;TG Flex for Less&quot;]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/WcUnydfJ49spUBjEFbazE4-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>We’re still evaluating the Bravia 9 II, but I’ll give you a little sneak-preview: Sony’s RGB LED flagship is one of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> of the year.</p><p>The brand’s spin on the all-new display technology — dubbed True RGB — serves as a reminder that Sony is one of the top TV-makers in the world. But if you were hoping to get your hands on the Bravia 9 II, you’ll have to make peace with its ultra-high price tag.</p><p>Fortunately, there’s another way to get a taste of Sony’s True RGB engineering at a much friendlier price: the <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Bravia 7 II</a>. Having tested the Bravia 7 II, we can confirm that it’s an impressive, mid-range alternative to its higher-end sibling. It's the set I recommend if you’re hesitant to splash out on the top TV in Sony’s lineup.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/X7i7NXCPPFY" allowfullscreen></iframe></div></div><h2 id="the-bravia-7-ii-is-the-mid-range-rgb-led-tv-to-beat-this-year">The Bravia 7 II is the mid-range RGB LED TV to beat this year</h2><p>As Sony’s most affordable True RGB TV, the Bravia 7 II may not be as impressive as the flagship model, but there’s an argument to be made that it’s better than the 2024 <a href="https://www.tomsguide.com/tvs/sony-bravia-9-tv-review">Bravia 9</a> (one of the best Mini-LED TVs I’ve ever seen).</p><p>Let’s take a look at how the Bravia 7 II stacks up against the original Bravia 9 and the all-new Bravia 9 II.</p><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Sony Bravia 7 II</strong></p></td><td  ><p><strong>Sony Bravia 9 II</strong></p></td><td  ><p><strong>Sony Bravia 9</strong></p></td></tr><tr><td class="firstcol " ><p>HDR Brightness (10%, in nits)</p></td><td  ><p>2,078</p></td><td  ><p>3,990</p></td><td  ><p>2,713</p></td></tr><tr><td class="firstcol " ><p>Rec. 2020 Gamut Coverage</p></td><td  ><p>87.2%</p></td><td  ><p>87.3%</p></td><td  ><p>78.02%</p></td></tr></tbody></table></div><p>As far as brightness goes, the Bravia 7 II peaks just below what we measured on the Bravia 9. This is plenty bright for most viewing environments, and while the Bravia 9 has some extra <em>oomph</em>, it’s not as significant as one might expect.</p><p>The real kicker is the Bravia 7 II’s ability to showcase color. Driven by Sony’s True RGB technology, it covers an impressive 87% of the BT.2020 color gamut. It’s a sizable step up from the Bravia 9 and right in line with the Bravia 9 II.</p><div class="product"><a data-dimension112="d10c2230-8539-11f1-ba4d-c199438f2c40" data-action="Deal Block" data-label="65" Bravia 7 II True RGB 4K TV" data-dimension48="65" Bravia 7 II True RGB 4K TV" data-dimension25="$1999" href="https://www.bestbuy.com/product/sony-65-class-bravia-7-ii-true-rgb-4k-hdr-google-tv-with-gemini/J7XSRH54W4" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="XAHA44ag88wbX7JJbYXA6T" name="Sony-Bravia-7-ii-True-RGB-TV-16-by-9-deal-block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/XAHA44ag88wbX7JJbYXA6T-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/sony-65-class-bravia-7-ii-true-rgb-4k-hdr-google-tv-with-gemini/J7XSRH54W4" target="_blank" rel="nofollow" data-dimension112="d10c2230-8539-11f1-ba4d-c199438f2c40" data-action="Deal Block" data-label='65" Bravia 7 II True RGB 4K TV' data-dimension48='65" Bravia 7 II True RGB 4K TV' data-dimension25="$1999">65" Bravia 7 II True RGB 4K TV: was $2599 now $1999</a></strong><br>Sony's mid-range True RGB TV blends cutting-edge display technology with Sony's superb picture processing. It also comes with a heaping helping of streaming- and -gaming-related features. Right now, the 65-inch version is on sale for $600 off at Best Buy.<a class="view-deal button" href="https://www.bestbuy.com/product/sony-65-class-bravia-7-ii-true-rgb-4k-hdr-google-tv-with-gemini/J7XSRH54W4" target="_blank" rel="nofollow" data-dimension112="d10c2230-8539-11f1-ba4d-c199438f2c40" data-action="Deal Block" data-label="65" Bravia 7 II True RGB 4K TV" data-dimension48="65" Bravia 7 II True RGB 4K TV" data-dimension25="$1999">View Deal</a></p></div><p>With a better array of RGB backlights with more-precise dimming, the Bravia 9 II is undoubtedly the better-performing TV. But make no mistake: The Bravia 7 II is packing a ton of horsepower under the hood.</p><p>Sony’s mid-range model also comes with a comparable set of features. Just like the Bravia 9 II, the 7 II supports 4K content at 120Hz across two of its four total HDMI inputs. You’re also getting Variable Refresh Rate (VRR), Dolby Vision support and the Google TV streaming platform right out of the box.</p><h2 id="sony-bravia-7-ii-outlook">Sony Bravia 7 II: outlook</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="5tPHvyuZ45bg5ZN2BKSRej" name="Sony-Bravia-7-II-True-RGB-TV" alt="Lifestyle image of Sony Bravia 7 II True RGB TV in living room environment" src="https://cdn.mos.cms.futurecdn.net/5tPHvyuZ45bg5ZN2BKSRej-1920-80.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: Sony)</span></figcaption></figure><div><blockquote><p>The Bravia 7 II feels closer in spirit to a high-end, luxury purchase than a middle-of-the road compromise.</p></blockquote></div><p>None of this is to suggest that the Bravia 9 II <em>isn’t</em> worth buying. Far from it. But as is the case with many mid-range OLED TVs (like the <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled-review">LG C6</a>), the Bravia 7 II feels closer in spirit to a high-end, luxury purchase than a middle-of-the road compromise.</p><p>Consider this: The <a href="https://www.bestbuy.com/product/sony-65-class-bravia-7-ii-true-rgb-4k-hdr-google-tv-with-gemini/J7XSRH54W4" target="_blank" rel="nofollow">65-inch Bravia 7 II just went on sale for $1,999 at Best Buy</a>. That’s $600 off its original price and only a couple hundred bucks more than the 65-inch C6 OLED.</p><p>The value proposition is even more compelling when you consider the top-shelf option. Right now, the <a href="https://www.bestbuy.com/product/sony-65-class-bravia-9-ii-true-rgb-4k-hdr-google-tv-with-gemini-and-anti-glare-screen/J7XSRH54TJ/sku/6674085" target="_blank" rel="nofollow">65-inch Bravia 9 II is $3,299 at Best Buy</a> — a whopping $1,300 more than the Bravia 7 II.</p><p>The Bravia 9 II might prove to be one of the best-performing TVs of the year, but the Bravia 7 II will almost certainly be the set I recommend more often between now and the holiday-shopping season. This True RGB has a chance to be a true sleeper hit.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePaJpW"></div>                            </div>                            <script src="https://kwizly.com/embed/ePaJpW.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/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Sony’s new Bravia 7 II has a secret weapon that makes it so good</a></li><li><a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs">I saw Sony's Bravia 9 II up close and it completely shattered my expectations — here’s why it’s the king of RGB TVs</a></li><li><a href="https://www.tomsguide.com/tvs/theres-a-better-cable-for-tvs-than-hdmi-but-heres-why-well-probably-never-use-it">There's a better cable for TVs than HDMI — but here's why we'll probably never use it</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ If PlayStation wants to succeed with all-digital, it needs to follow the Steam model — 5 ways it can win over gamers like me ]]></title>
                                                                                                <dc:content><![CDATA[ <p><a href="https://www.tomsguide.com/gaming/playstation/playstation-is-officially-killing-physical-discs-heres-when-they-disappear-and-what-that-means-for-you">PlayStation is officially killing physical discs</a>, and gamers aren’t happy. The company’s social media accounts can’t post anything without being slammed with negative comments, and even websites and influencers who are usually fans of PlayStation are taking them to task. Sony has yet to respond to any of this, which is likely making the situation worse. In short, this whole no-disc situation is a mess.</p><p>Sony says that digital now accounts for the vast majority of PlayStation game sales — hitting 85% in the most recent quarter — and it also nets a greater profit from digital purchases than physical, so it’s unlikely the company will reverse this controversial decision. However, if Sony wants to start (re)building trust with its fanbase, there are ways to make an all-digital future more palatable, namely by copying some of what has made Steam successful.</p><p>Steam has been a rousing success on PC for many years. Though Valve doesn’t offer physical games, it provides users with plenty of incentives to remain satisfied with their digital purchases and with the platform overall. I don’t expect Sony to completely copy Steam, but it can adopt similar policies if it ends disc production in January 2028.</p><p>Here are five practical, realistic adaptations Sony can take to make PSN more player-friendly.</p><h2 id="more-frequent-and-enticing-sales">More frequent (and enticing) sales</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="ykRy32GfJmDiDjqymNueJf" name="shutterstock_703264159.jpg" alt="Steam storefront on laptop screen." src="https://cdn.mos.cms.futurecdn.net/ykRy32GfJmDiDjqymNueJf-1920-80.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>While PC gaming has higher upfront costs than consoles, it has become more affordable over time thanks to frequent Steam sales. Steam keeps its online store thriving with major seasonal sales and near-constant publisher promotions. Games also drop in price faster and more aggressively. This is why Steam users (like me) end up with libraries of hundreds of games. It’s hard to pass up a title when it’s just a few bucks!</p><p>The PlayStation Store has its own sales events, such as Days of Play, Black Friday, and other holiday sales, along with weekly deals. However, full-price windows for new games are longer, and we usually only see extreme deep discounts when a game is all but forgotten. I’ve taken advantage of PSN sales and snatched some good titles, but the sales and discounts still don’t match those of Steam.</p><p>In an all-digital world, Sony would have complete control over pricing. Using Steam-style volume-driven discounting on the back catalog (and even earlier on mid-tier titles) would encourage more impulse and library-building purchases, rather than people either waiting for huge discounts or skipping entirely.</p><h2 id="a-true-try-before-you-fully-commit-refund-policy">A true “try before you fully commit” refund policy</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:957px;"><p class="vanilla-image-block" style="padding-top:67.71%;"><img id="sEEJ2czKyykbmZiNdbohSe" name="Screenshot 2025-09-04 100754" alt="Hollow Knight: Silksong store page on Steam" src="https://cdn.mos.cms.futurecdn.net/sEEJ2czKyykbmZiNdbohSe-1920-80.png" mos="" align="middle" fullscreen="" width="957" height="648" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Steam)</span></figcaption></figure><p>Right now, Steam arguably has the best refund policy in gaming. So long as you play a game for less than two hours total, you have 14 days to receive a full refund. This is a fantastic way to avoid purchasing a game that doesn’t click for you. While not technically a game demo, it pretty much serves the same “try before you buy” ethos.</p><p>PlayStation also has a 14-day refund policy, but it only applies if you haven’t downloaded a game. Once you start downloading, you no longer qualify for a refund unless the file is corrupt or you make a strong case to customer support that you purchased a game in error. This is frustrating enough now, but it will feel even worse if you can’t refund a digital title priced at $70–$80.</p><p>A playtime-based refund window, similar to Steam’s, would go a long way toward increasing purchase confidence without requiring Sony to change its curated store model. It’s actually wild that Sony doesn’t already have a similar policy, but the company should try it now if it plans to go all-in on an all-digital console.</p><h2 id="stronger-household-family-sharing">Stronger household/family sharing</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="kq426XFE2gzifzQdVzDYDU" name="steam-home-page.jpg" alt="The home page for Steam" src="https://cdn.mos.cms.futurecdn.net/kq426XFE2gzifzQdVzDYDU-1920-80.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: Steam)</span></figcaption></figure><p>Sharing games is one of the main reasons gamers love physical media. While sharing digital content isn’t the same as handing a copy to someone else, Steam has a digital sharing policy that could work well for Sony.</p><p>On Steam, up to six family members can share a unified game library. Multiple members can play different games at the same time (you need multiple owned copies of the same game for concurrent play). </p><p>PlayStation has sharing features, but they’re more limited.</p><p>Console Sharing lets you log in to your account on a friend’s PS5 so they can play your games. Share Play lets you share your screen with a friend online and virtually hand them your controller. Though both of these methods work as advertised, a person cannot simply play games from another account on their own console in a straightforward way.</p><p>Expanding sharing options on PlayStation so a household can more easily share a digital library (with reasonable restrictions against abuse) wouldn’t be as seamless as lending someone a physical game, but it would at least offer gamers something comparable.</p><h2 id="better-discovery-tools">Better discovery tools</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qvJ6bN35U3ezruK5peaaZk" name="20211213092912.jpg" alt="The Matrix Awakens product listing on the PlayStation Store on PS5" src="https://cdn.mos.cms.futurecdn.net/qvJ6bN35U3ezruK5peaaZk-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>Steam does a fantastic job with its wishlists, price-drop notifications, “players also like,” and other recommendations driven by player data. PlayStation has its own versions of these, but they could be updated to be more useful.</p><p>The store’s recent lowest-price-in-30-days indicator is a good step, but Sony can go further with smarter recommendations, clearer sale calendars, and stronger wishlist alerts. That last one really needs work — I feel like wishlist alerts are random at best, if I get them at all.</p><p>Improving discovery is an easy way to help players find and buy more games within Sony’s closed ecosystem. If you want people to buy digital games, you need to make it easier for them to find games they would actually like.</p><h2 id="clearer-long-term-library-guarantees-and-library-management">Clearer long-term library guarantees and library management</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="6jGE4qfxVjSEbbRvJ77xYf" name="Steam collection library" alt="Steam collection library" src="https://cdn.mos.cms.futurecdn.net/6jGE4qfxVjSEbbRvJ77xYf-1920-80.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>One of the biggest reasons Sony is facing so much heat right now is the lack of clear guarantees about long-term access to the games players buy. Without physical discs, players worry more about access, storage, and account longevity. This is something that <a href="https://www.tomsguide.com/gaming/playstation/customers-need-assurances-their-games-wont-be-taken-from-them-after-purchase-stop-killing-games-on-playstations-decision-to-kill-physical-discs">Stop Killing Games founder Ross Scott</a> detailed when we interviewed him about Sony's decision to stop manufacturing discs.</p><p>Sony can learn from Steam’s emphasis on transparent licensing, robust cloud saves, and better tools for managing large digital libraries. Explicit, public commitments to offline play and long-term download availability would go a long way toward easing the anxiety surrounding digital-only ownership.</p><p>This is arguably the most important issue Sony has to address. People (rightly) want some form of ownership of the games they buy, especially when those games are digital. I’m not saying this will please everyone — some folks are certainly ride-or-die physical gamers — but clearer guarantees would ease much of the current anxiety.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eMVm7W"></div>                            </div>                            <script src="https://kwizly.com/embed/eMVm7W.js" async></script><h2 id="bottom-line">Bottom line</h2><p>I want to be clear that I am not dunking on Sony or PlayStation. The <a href="https://www.tomsguide.com/gaming/playstation/ps5-pro-review">PS5 Pro</a> is my primary gaming platform, after all. But as a PlayStation gamer since the early 2000s (thanks to the Gran Turismo 3 PS2 bundle!), I want to see the company do right by loyal customers like me.</p><p>PSN is not inherently broken. Sony already runs a polished, high-production store. However, things need to change when discs disappear. Adopting some of Steam’s more player-friendly policies would not only benefit gamers but also Sony, which would benefit from a more committed user base.</p><p>Sony will ultimately do what it believes is in its best interest. That said, prioritizing what customers want, genuinely listening, and addressing concerns would go a long way. Hopefully, Sony does right by its user base and makes an all-digital future on its consoles as acceptable as it is on Steam.</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/gaming/pc-gaming/my-wallet-gonna-cry-valve-gives-official-update-on-steam-machine-and-steam-frame-launch-but-still-wont-tell-us-the-price">Valve gives official update on Steam Machine and Steam Frame launch</a></li><li><a href="https://www.tomsguide.com/gaming/pc-gaming/steam-machine-reviews-are-here-even-valve-is-disappointed">Steam Machine reviews are here: 'Even Valve is disappointed'</a></li><li><a href="https://www.tomsguide.com/gaming/gaming-peripherals/steam-controller-review">The new Steam Controller is the only PC gamepad I'll ever need</a></li></ul><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=gaming&min_discount_ratio=0.95&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/LCxBaXIgdGFncywsLCwsLCwsLCwsLCwsDQosLFBvcnRhYmxlIGNoYXJnZXJzL3dpcmVsZXNzIGNoYXJnZXJzLCwsLCwsLCwsLCwsLCwNCiwsIldhdGVyIGJvdHRsZXMgKHN0YW5sZXlzLCBPd2FsYSwgSHlkcm8gZmxhc2ssIFlldGkpIiwsLCwsLCwsLCwsLCwsDQosLEhhbmQgaGVsZCBmYW5zLCwsLCwsLCwsLCwsLCwNCiwsLCwsLCwsLCwsLCwsLCwNCmhvbWUgb2ZmaWNlLCxvZmZpY2UgY2hhaXJzLCwsLCwsLCwsLCwsLCwNCiwsc3RhbmRpbmcgZGVza3MsLCwsLCwsLCwsLCwsLA0KLCxtb25pdG9ycywsLCwsLCwsLCwsLCwsDQosLEtleWJvYXJkcywsLCwsLCwsLCwsLCwsDQosLGRvY2tpbmcgc3RhdGlvbiwsLCwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpHYW1pbmcsLENvbnNvbGVzLCwsLCwsLCwsLCwsLCwNCiwsQWNjZXNzb3JpZXMsLCwsLCwsLCwsLCwsLA0KLCxHYW1lcywsLCwsLCwsLCwsLCwsDQosLENvdWxkIGluY2x1ZGUgTGVnbz8sLCwsLCwsLCwsLCwsLA==")));              const text = preloadedCSV;              const parsed = parseCSV(text);                            const rowsByName = {};              let filterStart = -1;              parsed.forEach((rc, i) => {                 if (rc && rc.length > 0 && rc[0]) rowsByName[rc[0]] = rc;                 if (rc && rc.length > 0 && rc[0] === 'Filter buttons') filterStart = i;              });                            const cols = [];              if(rowsByName['Roundel text']) {                const headerRow = rowsByName['Roundel text'];                for(let col = 1; col < headerRow.length; col++) {                   let label = headerRow[col];                   if (!label) continue;                                      let q = rowsByName['Search Query'] && rowsByName['Search Query'][col] ? rowsByName['Search Query'][col] : '';                   let img = rowsByName['Roundel image'] && rowsByName['Roundel image'][col] ? rowsByName['Roundel image'][col] : '';                   let ds = rowsByName['Discount Amount'] && rowsByName['Discount Amount'][col] ? rowsByName['Discount Amount'][col] : '';                   let pr = rowsByName['Price Range'] && rowsByName['Price Range'][col] ? rowsByName['Price Range'][col] : '';                   let rt = rowsByName['Retailer'] && rowsByName['Retailer'][col] ? rowsByName['Retailer'][col] : '';                   let ot = rowsByName['Offer Type'] && rowsByName['Offer Type'][col] ? rowsByName['Offer Type'][col] : '';                                      let filters = [];                   if(filterStart > 0) {                     for(let r = filterStart + 1; r < parsed.length; r++) {                         if(!parsed[r] || parsed[r][0] === 'Notes' || parsed[r][0] === 'Categories to consider') break;                         let f = parsed[r][col];                         if(f) filters.push(f);                     }                   }                   cols.push({ label, img, q, ds, pr, rt, ot, filters });                }              }              this.carouselData = cols;              if (this.carouselData && this.carouselData.length > 0) {                 const isMatched = this.carouselData.some(c => c.q === this.currentQuery || c.label === this.currentQuery);                 if (!isMatched) {                    const first = this.carouselData[0];                    this.currentQuery = first.q || first.label;                    if (this.priceFilter) this.priceFilter.value = 'all';                    if (this.customPriceMin) this.customPriceMin.value = '';                    if (this.customPriceMax) this.customPriceMax.value = '';                    let dPr = first.pr || 'all';                    if (typeof dPr === 'string' && dPr !== 'all') {                       let prLower = dPr.toLowerCase();                       if (prLower.includes('min') || prLower.includes('over')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else if (prLower.includes('max') || prLower.includes('under')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       }                    }                    let dAm = '0';                    if(first.ds && typeof first.ds === 'string') {                       let m = first.ds.match(/(\d+)/);                       if(m) dAm = m[1];                    }                    if (this.discountFilter) this.discountFilter.value = dAm;                    if (this.offerTypeSelect) this.offerTypeSelect.value = first.ot || '';                    if (this.retailerSelect) this.retailerSelect.value = first.rt || '';                    this.selectedBrands = [];                    if (this.brandDropdown) {                        const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                        chks.forEach(chk => chk.checked = false);                    }                    if (this.searchInput) this.searchInput.value = this.currentQuery;                 }              }              this.renderCarouselUI();          } catch(e){ console.warn(e); }        }                renderCarouselUI() {           const roundelWrapper = this.root.querySelector('.tg-df-carousel-roundels');           if(!roundelWrapper || !this.carouselData) return;                      let html = '';           this.carouselData.forEach(r => {              const q = r.q || r.label;              const isActive = (this.currentQuery === q || this.currentQuery === r.label) ? 'active' : '';              const imgHtml = r.img ? `\x3Cimg src="${r.img}" alt="${r.label}" />` : `\x3Csvg width="32" height="32" fill="#1F69FF" viewBox="0 0 24 24">\x3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\x3C/svg>`;              html += `                \x3Cdiv class="tg-df-roundel tg-df-carousel-cat ${isActive}" data-label="${this.escapeHTML(r.label)}">                  \x3Cdiv class="tg-df-roundel-img-box">${imgHtml}\x3C/div>                  \x3Cspan class="tg-df-roundel-label">${this.escapeHTML(r.label)}\x3C/span>                \x3C/div>              `;           });           roundelWrapper.innerHTML = html;                      // Rebind clicks           const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');           roundels.forEach(rNode => {             rNode.addEventListener('click', () => {                const r = this.carouselData.find(c => c.label === rNode.getAttribute('data-label'));                 if(!r) return;                                  if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: r.label,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = r.q || r.label;                const labelTitle = this.root.querySelector('#tg-df-carousel-title-label');                if (labelTitle) labelTitle.textContent = 'Best ' + this.currentQuery;                if (this.priceFilter) this.priceFilter.value = 'all';                if (this.customPriceMin) this.customPriceMin.value = '';                if (this.customPriceMax) this.customPriceMax.value = '';                let dPr = r.pr || 'all';                if (typeof dPr === 'string' && dPr !== 'all') {                   let prLower = dPr.toLowerCase();                   if (prLower.includes('min') || prLower.includes('over')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMin) this.customPriceMin.value = m[1];                   } else if (prLower.includes('max') || prLower.includes('under')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMax) this.customPriceMax.value = m[1];                   }                }                                let discountAmount = '0';                if(r.ds && typeof r.ds === 'string') {                   let m = r.ds.match(/(\d+)/);                   if(m) discountAmount = m[1];                }                if (this.discountFilter) this.discountFilter.value = discountAmount;                if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                                // Clear brands                    this.selectedBrands = [];                    if (this.brandDropdown) {                    const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                    chks.forEach(chk => chk.checked = false);                }                                if (this.searchInput) this.searchInput.value = this.currentQuery;                                roundels.forEach(ro => ro.classList.remove('active'));                if (rNode) rNode.classList.add('active');                                this.renderCarouselFilters(r);                this.fetchDeals(this.currentQuery);             });           });                      // Auto-highlight active           const activeR = this.carouselData.find(c => c.q === this.currentQuery || c.label === this.currentQuery);           if(activeR) this.renderCarouselFilters(activeR);        }                renderCarouselFilters(r) {           const filtersWrap = this.root.querySelector('.tg-df-carousel-filters-wrap');           if(!filtersWrap) return;                      let html = `\x3Cbutton class="tg-df-carousel-filter-btn" data-type="all">All\x3C/button>`;                      r.filters.forEach(f => {              let fL = f.toLowerCase();              let icon = '';              let logic = `data-type="custom" data-v="${this.escapeHTML(f)}"`;              if (fL === 'amazon deals' || fL === 'prime deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>`;              } else if (fL === 'lightning deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>`;              } else {                 if (fL.includes('lightning')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap">\x3Cpolygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2">\x3C/polygon>\x3C/svg>`;                 } else if (fL.includes('% off')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>`;                 } else if (fL.includes('under') || fL.includes('min ')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>`;                 }                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>${icon} ${this.escapeHTML(f)}\x3C/button>`;              }           });                      filtersWrap.innerHTML = html;                      const btns = filtersWrap.querySelectorAll('button');           btns.forEach(b => {             b.addEventListener('click', () => {                const type = b.getAttribute('data-type');                if (type === 'custom') {                   const v = b.getAttribute('data-v');                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-custom-${(v||'').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`, name: 'Custom Filter', label: v });                }                if (type === 'all') {                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: 'filter-clear-all', name: 'Clear all', label: 'Clear all filters' });                   // reset everything                   btns.forEach(btn => btn.classList.remove('active'));                   b.classList.add('active');                                      // Reset prices                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   if (this.brandDropdown) {                     const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                     chks.forEach(chk => chk.checked = false);                   }                } else {                   const v = b.getAttribute('data-v');                   const fL = v.toLowerCase();                                      let mapRet = ['amazon', 'walmart', 'best buy', 'target', 'john lewis', 'currys', 'argos'];                   const getCategory = (s) => {                      if (s === 'lightning deals' || s === 'amazon deals' || s === 'prime deals') return 'offer';                      if (s.includes('% off')) return 'discount';                      if (s.includes('under') || s.includes('over') || s.includes('min') || s.includes('max')) return 'price';                      if (mapRet.includes(s)) return 'retailer';                      return 'brand';                   };                   const cat = getCategory(fL);                   const wasActive = b.classList.contains('active');                   if (cat !== 'brand') {                      btns.forEach(btn => {                          if (btn === b) return;                          if (btn.getAttribute('data-type') === 'all') return;                          const bV = btn.getAttribute('data-v');                          if (!bV) return;                          if (getCategory(bV.toLowerCase()) === cat) btn.classList.remove('active');                      });                   }                   if (wasActive) b.classList.remove('active');                   else b.classList.add('active');                   let anyActive = Array.from(btns).some(btn => btn !== btns[0] && btn.classList.contains('active'));                   if (!anyActive) {                       btns[0].click();                       return;                   } else {                       btns[0].classList.remove('active');                   }                                      if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   btns.forEach(btn => {                       if (!btn.classList.contains('active') || btn.getAttribute('data-type') === 'all') return;                       const vv = btn.getAttribute('data-v');                       const vl = vv.toLowerCase();                                              if (vl === 'lightning deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_lightning';                       } else if (vl === 'amazon deals' || vl === 'prime deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_prime';                       } else if (vl.includes('% off')) {                          let m = vl.match(/(\d+)%/);                          if (m && this.discountFilter) this.discountFilter.value = m[1];                       } else if (vl.includes('under') || vl.includes('max')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       } else if (vl.includes('min') || vl.includes('over')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else {                          let foundR = mapRet.find(x => x === vl);                          if (foundR) {                             let realR = ['Amazon', 'Walmart', 'Best Buy', 'Target', 'John Lewis', 'Currys', 'Argos'].find(x => x.toLowerCase() === vl);                             if (this.retailerSelect) this.retailerSelect.value = realR;                          } else {                             this.selectedBrands.push(vv);                          }                       }                   });                                      if (this.brandDropdown) {                       const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                       chks.forEach(c => c.checked = this.selectedBrands.includes(c.value));                   }                                      if (r.pr && typeof r.pr === 'string') {                       let prL = r.pr.toLowerCase();                       if (prL.includes('under $')) {                           let m = prL.match(/under \$(\d+)/i);                           if (m && this.customPriceMax && !this.customPriceMax.value) this.customPriceMax.value = m[1];                       }                   }                }                                this.fetchDeals(this.currentQuery);             });           });                      // default to highlighting first           btns[0].classList.add('active');        }async fetchDeals(query, append = false) {          if (!append) {             this.showLoading();             this.deals = [];             this.displayLimit = this.getRowsLimit();          } else {             this.displayLimit += this.getRowsLimit();          }                    try {            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad(append);            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query, append);               } else {                 await this.fetchHawkDeals(query, append);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query, append);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const url = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let dynamicBrandsCounts = {};                    targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                   dynamicBrandsCounts[data.brand] = (dynamicBrandsCounts[data.brand] || 0) + 1;                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || ''                });             });          });                    const airedaleBrandsList = Object.keys(dynamicBrandsCounts).map(b => ({              formatted_value: b,              count: dynamicBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query, append = false) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (append && this.deals.length > 0) {            url.searchParams.append('offset', this.deals.length.toString());          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : (this.getViewMode() === 'savings_squad' ? 'date_desc' : 'discount_desc');          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin-top:8px;">${_div}                ${_div}              ${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)} (${this.airedaleTagCounts[tag] || 0})${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = isSavingsSquadMode ? 'View Deal' : 'Check Price';              if (isSavingsSquadMode) {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                  ${_div}                `;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = `                \x3Cdiv class="tg-df-card-merchant-wrapper">                  \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                ${_div}                \x3Cdiv class="tg-df-card-price-group">                  ${isSavingsSquadMode ? '' : `                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                  `}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description">\x3Cp>${this.escapeHTML(deal.description)}${_p}${_div}` : ''}                      ${_div}                    ${_div}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>${deal.isCheckPrice ? 'Check Price' : 'View Deal'}${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cp style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 12px; line-height: 1.4;">${this.escapeHTML(deal.description)}${_p}` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${priceGroupHtml}                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta ${isSavingsSquadMode ? 'tg-df-cta-savings-squad' : ''}" style="text-decoration: none;">${ctaText}${_a}                  ${_div}                ${_div}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit || ((this.getViewMode() === 'carousel' || this.getViewMode() === 'auto') && displayDeals.length > 0 && displayDeals.length % ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12) === 0)) {            if (this.getViewMode() === 'carousel') {               dealsHtml += `                 \x3Cbutton type="button" class="tg-df-load-more-card tg-df-load-more">                   \x3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;">\x3Cpath d="M5 12h14">\x3C/path>\x3Cpath d="m12 5 7 7-7 7">\x3C/path>\x3C/svg>                   Load More                 ${_button}               `;            } else {               dealsHtml += `                 \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                   \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer; display: flex; align-items: center;">Load More${_button}                 ${_div}               `;            }          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let rightChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             let leftChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-left');             if (this.getViewMode() === 'carousel') {                 // The observer set up in setupScrollListeners handles visibility.                 if (rightChevron) rightChevron.style.display = 'flex';                 if (leftChevron) leftChevron.style.display = 'none'; // reset correctly             } else {                 if (rightChevron) rightChevron.style.display = 'none';                 if (leftChevron) leftChevron.style.display = 'none';             }          }                    const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', async () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              if (displayDeals.length <= this.displayLimit) {                 loadMoreBtn.innerHTML = `                  <svg class="tg-df-spinner" style="width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 8px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/></svg>                  Loading...                 `;                 loadMoreBtn.disabled = true;                 await this.fetchDeals(this.currentQuery, true);              } else {                 this.displayLimit += this.getRowsLimit();                 this.render();              }            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            setTimeout(() => {               if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');               document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));            }, 50);          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/gaming/playstation/if-playstation-wants-to-succeed-with-all-digital-it-needs-to-follow-the-steam-model-5-ways-it-can-win-over-gamers-like-me</link>
                                                                            <description>
                            <![CDATA[ PlayStation appears to be going all-in on digital games. To make this easier on gamers, it should implement some of Steam's policies. Here's what Sony can do. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">RZxxLUXKiNi3NuokYLVCbf</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/y5T6EzpDkzjkm8NbGVewWW-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 18 Jul 2026 15:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Playstation]]></category>
                                                    <category><![CDATA[Gaming]]></category>
                                                                                                <author><![CDATA[ tony.polanco@futurenet.com (Tony Polanco) ]]></author>                    <dc:creator><![CDATA[ Tony Polanco ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/atzRNqFt5wYgEUPBDahWsD-320-70.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/y5T6EzpDkzjkm8NbGVewWW-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The PlayStation logo on a PS5 Pro]]></media:description>                                                            <media:text><![CDATA[The PlayStation logo on a PS5 Pro]]></media:text>
                                <media:title type="plain"><![CDATA[The PlayStation logo on a PS5 Pro]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/y5T6EzpDkzjkm8NbGVewWW-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p><a href="https://www.tomsguide.com/gaming/playstation/playstation-is-officially-killing-physical-discs-heres-when-they-disappear-and-what-that-means-for-you">PlayStation is officially killing physical discs</a>, and gamers aren’t happy. The company’s social media accounts can’t post anything without being slammed with negative comments, and even websites and influencers who are usually fans of PlayStation are taking them to task. Sony has yet to respond to any of this, which is likely making the situation worse. In short, this whole no-disc situation is a mess.</p><p>Sony says that digital now accounts for the vast majority of PlayStation game sales — hitting 85% in the most recent quarter — and it also nets a greater profit from digital purchases than physical, so it’s unlikely the company will reverse this controversial decision. However, if Sony wants to start (re)building trust with its fanbase, there are ways to make an all-digital future more palatable, namely by copying some of what has made Steam successful.</p><p>Steam has been a rousing success on PC for many years. Though Valve doesn’t offer physical games, it provides users with plenty of incentives to remain satisfied with their digital purchases and with the platform overall. I don’t expect Sony to completely copy Steam, but it can adopt similar policies if it ends disc production in January 2028.</p><p>Here are five practical, realistic adaptations Sony can take to make PSN more player-friendly.</p><h2 id="more-frequent-and-enticing-sales">More frequent (and enticing) sales</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="ykRy32GfJmDiDjqymNueJf" name="shutterstock_703264159.jpg" alt="Steam storefront on laptop screen." src="https://cdn.mos.cms.futurecdn.net/ykRy32GfJmDiDjqymNueJf-1920-80.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>While PC gaming has higher upfront costs than consoles, it has become more affordable over time thanks to frequent Steam sales. Steam keeps its online store thriving with major seasonal sales and near-constant publisher promotions. Games also drop in price faster and more aggressively. This is why Steam users (like me) end up with libraries of hundreds of games. It’s hard to pass up a title when it’s just a few bucks!</p><p>The PlayStation Store has its own sales events, such as Days of Play, Black Friday, and other holiday sales, along with weekly deals. However, full-price windows for new games are longer, and we usually only see extreme deep discounts when a game is all but forgotten. I’ve taken advantage of PSN sales and snatched some good titles, but the sales and discounts still don’t match those of Steam.</p><p>In an all-digital world, Sony would have complete control over pricing. Using Steam-style volume-driven discounting on the back catalog (and even earlier on mid-tier titles) would encourage more impulse and library-building purchases, rather than people either waiting for huge discounts or skipping entirely.</p><h2 id="a-true-try-before-you-fully-commit-refund-policy">A true “try before you fully commit” refund policy</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:957px;"><p class="vanilla-image-block" style="padding-top:67.71%;"><img id="sEEJ2czKyykbmZiNdbohSe" name="Screenshot 2025-09-04 100754" alt="Hollow Knight: Silksong store page on Steam" src="https://cdn.mos.cms.futurecdn.net/sEEJ2czKyykbmZiNdbohSe-1920-80.png" mos="" align="middle" fullscreen="" width="957" height="648" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Steam)</span></figcaption></figure><p>Right now, Steam arguably has the best refund policy in gaming. So long as you play a game for less than two hours total, you have 14 days to receive a full refund. This is a fantastic way to avoid purchasing a game that doesn’t click for you. While not technically a game demo, it pretty much serves the same “try before you buy” ethos.</p><p>PlayStation also has a 14-day refund policy, but it only applies if you haven’t downloaded a game. Once you start downloading, you no longer qualify for a refund unless the file is corrupt or you make a strong case to customer support that you purchased a game in error. This is frustrating enough now, but it will feel even worse if you can’t refund a digital title priced at $70–$80.</p><p>A playtime-based refund window, similar to Steam’s, would go a long way toward increasing purchase confidence without requiring Sony to change its curated store model. It’s actually wild that Sony doesn’t already have a similar policy, but the company should try it now if it plans to go all-in on an all-digital console.</p><h2 id="stronger-household-family-sharing">Stronger household/family sharing</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="kq426XFE2gzifzQdVzDYDU" name="steam-home-page.jpg" alt="The home page for Steam" src="https://cdn.mos.cms.futurecdn.net/kq426XFE2gzifzQdVzDYDU-1920-80.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: Steam)</span></figcaption></figure><p>Sharing games is one of the main reasons gamers love physical media. While sharing digital content isn’t the same as handing a copy to someone else, Steam has a digital sharing policy that could work well for Sony.</p><p>On Steam, up to six family members can share a unified game library. Multiple members can play different games at the same time (you need multiple owned copies of the same game for concurrent play). </p><p>PlayStation has sharing features, but they’re more limited.</p><p>Console Sharing lets you log in to your account on a friend’s PS5 so they can play your games. Share Play lets you share your screen with a friend online and virtually hand them your controller. Though both of these methods work as advertised, a person cannot simply play games from another account on their own console in a straightforward way.</p><p>Expanding sharing options on PlayStation so a household can more easily share a digital library (with reasonable restrictions against abuse) wouldn’t be as seamless as lending someone a physical game, but it would at least offer gamers something comparable.</p><h2 id="better-discovery-tools">Better discovery tools</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qvJ6bN35U3ezruK5peaaZk" name="20211213092912.jpg" alt="The Matrix Awakens product listing on the PlayStation Store on PS5" src="https://cdn.mos.cms.futurecdn.net/qvJ6bN35U3ezruK5peaaZk-1920-80.jpg" mos="" align="middle" fullscreen="" width="3840" height="2160" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>Steam does a fantastic job with its wishlists, price-drop notifications, “players also like,” and other recommendations driven by player data. PlayStation has its own versions of these, but they could be updated to be more useful.</p><p>The store’s recent lowest-price-in-30-days indicator is a good step, but Sony can go further with smarter recommendations, clearer sale calendars, and stronger wishlist alerts. That last one really needs work — I feel like wishlist alerts are random at best, if I get them at all.</p><p>Improving discovery is an easy way to help players find and buy more games within Sony’s closed ecosystem. If you want people to buy digital games, you need to make it easier for them to find games they would actually like.</p><h2 id="clearer-long-term-library-guarantees-and-library-management">Clearer long-term library guarantees and library management</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="6jGE4qfxVjSEbbRvJ77xYf" name="Steam collection library" alt="Steam collection library" src="https://cdn.mos.cms.futurecdn.net/6jGE4qfxVjSEbbRvJ77xYf-1920-80.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>One of the biggest reasons Sony is facing so much heat right now is the lack of clear guarantees about long-term access to the games players buy. Without physical discs, players worry more about access, storage, and account longevity. This is something that <a href="https://www.tomsguide.com/gaming/playstation/customers-need-assurances-their-games-wont-be-taken-from-them-after-purchase-stop-killing-games-on-playstations-decision-to-kill-physical-discs">Stop Killing Games founder Ross Scott</a> detailed when we interviewed him about Sony's decision to stop manufacturing discs.</p><p>Sony can learn from Steam’s emphasis on transparent licensing, robust cloud saves, and better tools for managing large digital libraries. Explicit, public commitments to offline play and long-term download availability would go a long way toward easing the anxiety surrounding digital-only ownership.</p><p>This is arguably the most important issue Sony has to address. People (rightly) want some form of ownership of the games they buy, especially when those games are digital. I’m not saying this will please everyone — some folks are certainly ride-or-die physical gamers — but clearer guarantees would ease much of the current anxiety.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eMVm7W"></div>                            </div>                            <script src="https://kwizly.com/embed/eMVm7W.js" async></script><h2 id="bottom-line">Bottom line</h2><p>I want to be clear that I am not dunking on Sony or PlayStation. The <a href="https://www.tomsguide.com/gaming/playstation/ps5-pro-review">PS5 Pro</a> is my primary gaming platform, after all. But as a PlayStation gamer since the early 2000s (thanks to the Gran Turismo 3 PS2 bundle!), I want to see the company do right by loyal customers like me.</p><p>PSN is not inherently broken. Sony already runs a polished, high-production store. However, things need to change when discs disappear. Adopting some of Steam’s more player-friendly policies would not only benefit gamers but also Sony, which would benefit from a more committed user base.</p><p>Sony will ultimately do what it believes is in its best interest. That said, prioritizing what customers want, genuinely listening, and addressing concerns would go a long way. Hopefully, Sony does right by its user base and makes an all-digital future on its consoles as acceptable as it is on Steam.</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/gaming/pc-gaming/my-wallet-gonna-cry-valve-gives-official-update-on-steam-machine-and-steam-frame-launch-but-still-wont-tell-us-the-price">Valve gives official update on Steam Machine and Steam Frame launch</a></li><li><a href="https://www.tomsguide.com/gaming/pc-gaming/steam-machine-reviews-are-here-even-valve-is-disappointed">Steam Machine reviews are here: 'Even Valve is disappointed'</a></li><li><a href="https://www.tomsguide.com/gaming/gaming-peripherals/steam-controller-review">The new Steam Controller is the only PC gamepad I'll ever need</a></li></ul><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=gaming&min_discount_ratio=0.95&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function 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[ A PlayStation 6 without a disc drive? I’m done with Sony ]]></title>
                                                                                                <dc:content><![CDATA[ <p>It seems like there’s no going back. I initially thought that <a href="https://www.tomsguide.com/gaming/playstation/playstation-is-officially-killing-physical-discs-heres-when-they-disappear-and-what-that-means-for-you">Sony’s decision to phase out physical discs for PlayStation 5</a> had such a long run-in time that there was deliberate wiggle room for a slightly embarrassing U-turn before 2028.</p><p>However, <a href="https://www.theverge.com/games/961203/sony-austria-thalgau-end-disc-production-microlenses-instead" target="_blank">The Verge</a> is reporting that <strong>Sony’s Thalgau plant is already being repurposed to make optical microlenses instead</strong>. In other words, this wasn’t testing the waters with an announcement and checking for feedback — this was<em> fait accompli</em>: a decision already decided upon, with loyal customers the last to know.</p><p>No wonder the company’s popular Xbox-baiting game loaning video from the PS4 era is now <a href="https://www.youtube.com/watch?v=uMrL8jwhMXg" target="_blank">being thrown back in its corporate face</a>. Despite effectively taking brutal advantage of Microsoft’s anti-consumer error with a viral marketing video, Sony got to a very similar place in the end. It just took them longer.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/kWSIFh8ICaA" allowfullscreen></iframe></div></div><p>Assuming there is no room for the expensive U-turn that Microsoft ended up screeching around 13 years ago, then that’s the end of the road for me with PlayStation. </p><p><strong>It’s not that I’m averse to digital gaming, I just have very little faith in Sony’s ability to deliver it at a reasonable price.</strong></p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eMVm7W"></div>                            </div>                            <script src="https://kwizly.com/embed/eMVm7W.js" async></script><h2 id="the-worst-of-both-worlds">The worst of both worlds</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:1890px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="QtC5XrBKmnRNJDnKmAV3QD" name="PS5 Gaming TV.jpg" alt="Ratchet & Clank being played on a PS5 and a Sony Bravia gaming TV." src="https://cdn.mos.cms.futurecdn.net/QtC5XrBKmnRNJDnKmAV3QD-1920-80.jpg" mos="" align="middle" fullscreen="" width="1890" height="1063" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>In theory, I can see the case for a digital-only console. A PS6 without a disc drive should be cheaper to manufacture (though that doesn’t mean cheap, given the soaring price of hardware), and as a PC gamer, I haven’t owned a computer with a disc drive in well over a decade.</p><p>But I consciously made the opposite choice when I pre-ordered my day-one PS5: <strong>I paid extra for the model with a disc drive, safe in the knowledge that over the long run, the extra cost would be recouped</strong>, after borrowing and trading in games had been taken into account. </p><p>Which isn’t to say I don’t see the benefit of digital. While nine times out of ten, I would buy the disc version for resale value and the ability to loan my favourite games to friends, I still would buy from the PlayStation store occasionally. After all, you can’t beat the convenience of instantly accessing a game no matter what time of day or night. The PlayStation Store never sleeps.</p><p>Indeed, 99% of my PC games come digitally via Steam, and it works brilliantly. But it’s not a like-for-like comparison, because ironically <strong>part of what makes Steam such a popular option is its competition</strong>. There’s no shortage of places to buy PC games from the Epic Store to GOG, so Steam can’t rest on its laurels: its prices have to stay competitive, especially during the frequent sales.</p><p>Sony could replicate this, allowing third-party game stores on PS6, but why would it? If there’s only one place to buy games, Sony can charge a healthy platform fee for every sale, safe in the knowledge that there’s no alternative (Valve gets away with <a href="https://steamcommunity.com/groups/steamworks/announcements/detail/1697191267930157838" target="_blank">between 20 and 30%</a> depending on sales figures, despite gamers being able to vote with their feet).</p><p>There was outrage when it was announced that <a href="https://www.tomsguide.com/news/ps5-and-xbox-series-x-games-will-cost-dollar70-and-subscriptions-may-be-the-future">PS5 games would cost $70</a>, but at least people knew they could recoup some of the costs when finished. With development costs spiralling,<strong> it wouldn’t be surprising if PS6 pricing goes even higher</strong> — and we’ve already seen some evidence of this price inflation with the <a href="https://www.tomsguide.com/gaming/gta-6s-ultimate-edition-is-the-real-full-game-and-the-rumors-of-it-costing-usd100-were-true-after-all">MSRP of $80 plus</a> for the digital-first <a href="https://www.tomsguide.com/news/gta-6">GTA 6</a>.</p><p><strong>Without physical game shops to compete with, Sony has far more control over sales too</strong>. The company can reduce both frequency and discount percentage, safe in the knowledge that <strong>you’ve got no alternative</strong>. If you want the game, you’ll have to pay whatever Sony wants or go without.</p><p>That’s not even considering the other downsides of digital-only games. What if your account gets hacked and Sony bans you? You’ve lost access to all the games you’d paid for. What if Sony decides to remove a game before you downloaded it? Tough luck, it’s gone forever. That’s why <a href="https://www.gamespot.com/articles/ps4s-with-pre-installed-pt-listed-on-ebay-for-as-m/1100-6426971/" target="_blank">PS4 consoles with the deleted PT demo were listed on Ebay for over $1,000 a decade ago</a>.</p><h2 id="farewell-playstation">Farewell PlayStation</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="ecidnCAguSkpYaD5oKosUH" name="TG_PS5_7.jpg" alt="PS5" src="https://cdn.mos.cms.futurecdn.net/ecidnCAguSkpYaD5oKosUH-1920-80.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>With the <a href="https://www.tomsguide.com/gaming/playstation/ps6-price-could-be-in-the-same-stratosphere-as-the-steam-machine-based-on-new-leak">expected price of PS6 blowing past even those PT scalping prices</a>, I was already contemplating opting out of a Sony console for the first time since I was at university with my trusty GameCube. </p><p>This move seals the deal. I’ll keep playing PS5 games, but once publishers stop making them and I finish my substantial backlog, <strong>I’ll turn off a PlayStation console for the last time</strong>. If I have to play digital-only, my PC and Steam Deck will get all the love going forward. Thanks anyway, Sony.</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/gaming/playstation/customers-need-assurances-their-games-wont-be-taken-from-them-after-purchase-stop-killing-games-on-playstations-decision-to-kill-physical-discs">We spoke to Stop Killing Games about PlayStation killing discs</a></li><li><a href="https://www.tomsguide.com/gaming/steam-machine-vs-ps5-pro-theres-a-clear-winner-for-most-gamers">Steam Machine vs. PS5 Pro — there’s a clear winner</a></li><li><a href="https://www.tomsguide.com/gaming/splatoon-raiders-combines-ink-splatting-and-treasure-hunting-and-im-already-hooked">Splatoon Raiders hands-on preview: I'm already hooked</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/gaming/a-playstation-6-without-a-disc-drive-im-done-with-sony</link>
                                                                            <description>
                            <![CDATA[ I’m not averse to digital gaming, but I don’t have much faith in Sony’s ability to deliver it at a fair price. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">4vGi6VscabZ3YMiWAh5pKV</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/FY4VGTzod8PrjU4mryi8hn-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 06 Jul 2026 09:10:10 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Gaming]]></category>
                                                    <category><![CDATA[Playstation]]></category>
                                                                                                                    <dc:creator><![CDATA[ Alan Martin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xMmhmPSssqZHHFWveDgSs7-320-70.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/FY4VGTzod8PrjU4mryi8hn-1920-80.jpg">
                                                            <media:credit><![CDATA[Rokas Tenys | Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[How to watch Super Bowl with your PS5]]></media:description>                                                            <media:text><![CDATA[How to watch Super Bowl with your PS5]]></media:text>
                                <media:title type="plain"><![CDATA[How to watch Super Bowl with your PS5]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/FY4VGTzod8PrjU4mryi8hn-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It seems like there’s no going back. I initially thought that <a href="https://www.tomsguide.com/gaming/playstation/playstation-is-officially-killing-physical-discs-heres-when-they-disappear-and-what-that-means-for-you">Sony’s decision to phase out physical discs for PlayStation 5</a> had such a long run-in time that there was deliberate wiggle room for a slightly embarrassing U-turn before 2028.</p><p>However, <a href="https://www.theverge.com/games/961203/sony-austria-thalgau-end-disc-production-microlenses-instead" target="_blank">The Verge</a> is reporting that <strong>Sony’s Thalgau plant is already being repurposed to make optical microlenses instead</strong>. In other words, this wasn’t testing the waters with an announcement and checking for feedback — this was<em> fait accompli</em>: a decision already decided upon, with loyal customers the last to know.</p><p>No wonder the company’s popular Xbox-baiting game loaning video from the PS4 era is now <a href="https://www.youtube.com/watch?v=uMrL8jwhMXg" target="_blank">being thrown back in its corporate face</a>. Despite effectively taking brutal advantage of Microsoft’s anti-consumer error with a viral marketing video, Sony got to a very similar place in the end. It just took them longer.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/kWSIFh8ICaA" allowfullscreen></iframe></div></div><p>Assuming there is no room for the expensive U-turn that Microsoft ended up screeching around 13 years ago, then that’s the end of the road for me with PlayStation. </p><p><strong>It’s not that I’m averse to digital gaming, I just have very little faith in Sony’s ability to deliver it at a reasonable price.</strong></p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eMVm7W"></div>                            </div>                            <script src="https://kwizly.com/embed/eMVm7W.js" async></script><h2 id="the-worst-of-both-worlds">The worst of both worlds</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:1890px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="QtC5XrBKmnRNJDnKmAV3QD" name="PS5 Gaming TV.jpg" alt="Ratchet & Clank being played on a PS5 and a Sony Bravia gaming TV." src="https://cdn.mos.cms.futurecdn.net/QtC5XrBKmnRNJDnKmAV3QD-1920-80.jpg" mos="" align="middle" fullscreen="" width="1890" height="1063" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>In theory, I can see the case for a digital-only console. A PS6 without a disc drive should be cheaper to manufacture (though that doesn’t mean cheap, given the soaring price of hardware), and as a PC gamer, I haven’t owned a computer with a disc drive in well over a decade.</p><p>But I consciously made the opposite choice when I pre-ordered my day-one PS5: <strong>I paid extra for the model with a disc drive, safe in the knowledge that over the long run, the extra cost would be recouped</strong>, after borrowing and trading in games had been taken into account. </p><p>Which isn’t to say I don’t see the benefit of digital. While nine times out of ten, I would buy the disc version for resale value and the ability to loan my favourite games to friends, I still would buy from the PlayStation store occasionally. After all, you can’t beat the convenience of instantly accessing a game no matter what time of day or night. The PlayStation Store never sleeps.</p><p>Indeed, 99% of my PC games come digitally via Steam, and it works brilliantly. But it’s not a like-for-like comparison, because ironically <strong>part of what makes Steam such a popular option is its competition</strong>. There’s no shortage of places to buy PC games from the Epic Store to GOG, so Steam can’t rest on its laurels: its prices have to stay competitive, especially during the frequent sales.</p><p>Sony could replicate this, allowing third-party game stores on PS6, but why would it? If there’s only one place to buy games, Sony can charge a healthy platform fee for every sale, safe in the knowledge that there’s no alternative (Valve gets away with <a href="https://steamcommunity.com/groups/steamworks/announcements/detail/1697191267930157838" target="_blank">between 20 and 30%</a> depending on sales figures, despite gamers being able to vote with their feet).</p><p>There was outrage when it was announced that <a href="https://www.tomsguide.com/news/ps5-and-xbox-series-x-games-will-cost-dollar70-and-subscriptions-may-be-the-future">PS5 games would cost $70</a>, but at least people knew they could recoup some of the costs when finished. With development costs spiralling,<strong> it wouldn’t be surprising if PS6 pricing goes even higher</strong> — and we’ve already seen some evidence of this price inflation with the <a href="https://www.tomsguide.com/gaming/gta-6s-ultimate-edition-is-the-real-full-game-and-the-rumors-of-it-costing-usd100-were-true-after-all">MSRP of $80 plus</a> for the digital-first <a href="https://www.tomsguide.com/news/gta-6">GTA 6</a>.</p><p><strong>Without physical game shops to compete with, Sony has far more control over sales too</strong>. The company can reduce both frequency and discount percentage, safe in the knowledge that <strong>you’ve got no alternative</strong>. If you want the game, you’ll have to pay whatever Sony wants or go without.</p><p>That’s not even considering the other downsides of digital-only games. What if your account gets hacked and Sony bans you? You’ve lost access to all the games you’d paid for. What if Sony decides to remove a game before you downloaded it? Tough luck, it’s gone forever. That’s why <a href="https://www.gamespot.com/articles/ps4s-with-pre-installed-pt-listed-on-ebay-for-as-m/1100-6426971/" target="_blank">PS4 consoles with the deleted PT demo were listed on Ebay for over $1,000 a decade ago</a>.</p><h2 id="farewell-playstation">Farewell PlayStation</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="ecidnCAguSkpYaD5oKosUH" name="TG_PS5_7.jpg" alt="PS5" src="https://cdn.mos.cms.futurecdn.net/ecidnCAguSkpYaD5oKosUH-1920-80.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>With the <a href="https://www.tomsguide.com/gaming/playstation/ps6-price-could-be-in-the-same-stratosphere-as-the-steam-machine-based-on-new-leak">expected price of PS6 blowing past even those PT scalping prices</a>, I was already contemplating opting out of a Sony console for the first time since I was at university with my trusty GameCube. </p><p>This move seals the deal. I’ll keep playing PS5 games, but once publishers stop making them and I finish my substantial backlog, <strong>I’ll turn off a PlayStation console for the last time</strong>. If I have to play digital-only, my PC and Steam Deck will get all the love going forward. Thanks anyway, Sony.</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/gaming/playstation/customers-need-assurances-their-games-wont-be-taken-from-them-after-purchase-stop-killing-games-on-playstations-decision-to-kill-physical-discs">We spoke to Stop Killing Games about PlayStation killing discs</a></li><li><a href="https://www.tomsguide.com/gaming/steam-machine-vs-ps5-pro-theres-a-clear-winner-for-most-gamers">Steam Machine vs. PS5 Pro — there’s a clear winner</a></li><li><a href="https://www.tomsguide.com/gaming/splatoon-raiders-combines-ink-splatting-and-treasure-hunting-and-im-already-hooked">Splatoon Raiders hands-on preview: I'm already hooked</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I stress-tested Sony’s Bravia 7 II True RGB TV with the darkest scenes I could find — here’s what happened ]]></title>
                                                                                                <dc:content><![CDATA[ <p>After a week of testing it with the darkest scenes I could find, the Sony Bravia 7 II True RGB TV made me reconsider what today’s TVs are actually capable of in low light. I lined up the usual stress‑test material — everything from “The Batman” to “House of the Dragon” — and watched it alongside my Samsung Neo QLED TV to see how each set handled the same challenges. I wasn’t trying to crown a winner right away; I just wanted to see how differently two modern TVs approach the same problem.</p><p>In our <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Bravia 7 II review</a>, we praised the TV for its ability to deliver "really deep blacks" alongside "precisely managed highlights", but its handling of contrast goes deeper than that — what stood out to me was how the Bravia 7 II's True RGB backlight system treated near‑black detail. </p><p>Even before digging into side‑by‑side comparisons, it was clear Sony’s Bravia 7 II had a distinct way of presenting dark scenes that didn’t rely on simply brightening them. That contrast with the Mini LED-backed Samsung TV became more noticeable the longer I watched.</p><p>The main takeaway is that the Bravia 7 II doesn’t try to reshape dark scenes to make them easier to see — it leans into how they were graded. Sometimes that means a scene stays intentionally dim; other times it reveals details that are easy to miss on other sets. Taking into account how difficult it is to accurately capture the colour and brightness of all RGB TVs in photos, here's how the Bravia 7 II handled the darkest scenes I could throw at it.</p><h2 id="house-of-the-dragon">‘House of the Dragon’</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="YRVD5TruoDjvJpuNtsMoFV" name="7547" alt="House of the Dragon viewed on Sony Bravia 7 II" src="https://cdn.mos.cms.futurecdn.net/YRVD5TruoDjvJpuNtsMoFV-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Like many people who watched the first season of HBO’s “House of the Dragon”, I was incredibly frustrated by one episode that was almost impossible to sit through. I’m talking about episode seven, “Driftmark” — the one filmed and graded so darkly that I could barely tell what was happening on screen, even while watching on Samsung’s flagship at the time, the QN900A Neo QLED 8K TV. </p><p>In his <a href="https://www.youtube.com/watch?v=D83SXcguwBU" target="_blank">testing</a>, Vincent Teoh from the YouTube channel <a href="https://www.youtube.com/@hdtvtest" target="_blank">HDTVTest</a> revealed that some sections of the episode “barely went above 1 nit” of brightness, so it’s no surprise that my television’s backlighting system went haywire in an attempt to compensate for the darkness on display, resulting in extreme haloing and light bloom. The episode also reportedly caused many OLED TVs to auto-dim until the image was almost totally black.</p><p>The backlash from viewers was so intense that showrunner Ryan Condal would eventually comment on the matter, telling <a href="https://variety.com/2022/tv/news/house-of-the-dragon-finale-explained-aemond-luke-rhaenrya-daemon-season-2-1235412136/" target="_blank">Variety</a> that future seasons would “take much more into account the fact that we are making the show for people’s television sets versus in a perfectly calibrated movie theater environment.” In other words, the viewing experience was less than ideal for those of us without super expensive reference monitors.</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="8buSXg3YSZFfjkJrVrs8Mo" name="7546" alt="House of the Dragon viewed on Sony Bravia 7 II" src="https://cdn.mos.cms.futurecdn.net/8buSXg3YSZFfjkJrVrs8Mo-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Naturally, this particular episode was my first stop in testing the Bravia 7 II’s handling of dark scenes. Taking into consideration Sony’s claims that its new True RGB TV range is factory-calibrated, I went in expecting the Bravia 7 II to do a decent job. Sure enough, I was pleased by how I could now make out what was happening in one of the most problematic scenes, which features two characters walking and talking on a beach. While the scene was clearly filmed day for night and then graded down for the sake of additional darkness, shadow detail was now much more visible, and I was able to see the characters’ faces without much issue. Don’t get me wrong — it’s still a very dark scene, but the murkiness and overcompensating backlight from my previous experience was gone.</p><p>The Bravia 7 II’s exceptional tone‑mapping also deserves credit here. Sony’s XR processor does a good job of preserving what the filmmakers actually meant you to see, especially in tougher HDR scenes. Instead of letting deep shadows swallow everything, it balances the contrast just enough so textures in faces or the outline of a creature in a night shot remain visible without brightening the whole frame.</p><h2 id="the-batman">‘The Batman’</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="fGrGVamNQsUXbKXqpSxR36" name="7548" alt="The Batman displayed on Sony Bravia 7 II" src="https://cdn.mos.cms.futurecdn.net/fGrGVamNQsUXbKXqpSxR36-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I absolutely loved “The Batman” when I saw it in theatres, and was excited to watch it again once it hit 4K Ultra HD Blu-ray. While my current Samsung QN900C Neo QLED 8K TV did a decent job displaying most of the film, it did struggle with some of its darker scenes. The opening train station brawl, in particular, was one that caused my TV to raise the brightness until blacks became somewhat grey and washed out.</p><p>Predictably, the Bravia 7 II handled this scene far better, revealing far more shadow detail without dulling the film’s contrast‑heavy mood. The moment when Batman slowly emerges from the darkness to confront a group of thugs has always been a trouble spot on the aforementioned Samsung TV, yet here it finally looked as dark and dramatic as I imagine the filmmakers intended. </p><p>It’s likely that Sony’s XR Backlight Master Drive, which uses a proprietary dimming algorithm, played a big part in keeping “The Batman” moody and dark without letting the shadows turn into a murky blur. The automatic switch to Dolby Vision Bright could also have helped, lifting just enough detail out of the deepest parts of the frame without noticeably changing the film’s overall tone.</p><h2 id="only-god-forgives">‘Only God Forgives’</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="W8GAPp22uqPsoAqWpZB73C" name="7556" alt="Only God Forgives displayed on Sony Bravia 7 II" src="https://cdn.mos.cms.futurecdn.net/W8GAPp22uqPsoAqWpZB73C-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Next, I moved to a film that’s notoriously tough on both OLED and Mini‑LED displays: “Only God Forgives”, from director Nicolas Winding Refn. Its relentless swings between pitch‑black shadows and hyper‑saturated neon highlights often expose local‑dimming delays, where the backlight or pixels can’t react quickly enough, creating faint ghosting that pulls you out of the moment.</p><p>The film’s heavy use of harsh red lighting against deep darkness also pushes the red channel hard, sometimes leading to clipping that bleeds into surrounding objects and wipes out fine detail along edges and contours.</p><p>A big part of why these issues show up is simply how most TVs generate brightness in the first place. Mini‑LED sets rely on a white backlight, and WRGB OLEDs boost luminance with a white subpixel. When a scene suddenly demands intense, saturated colour — especially neon red — the TV often leans on that white source to hit the required brightness. In a film this stylised, that can wash out the purity of the colour, lift nearby blacks, and make edges look a little less clean than they should.</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="nNDbSZ7yaF5BEeE7ftqJV7" name="7554" alt="Only God Forgives displayed on Sony Bravia 7 II" src="https://cdn.mos.cms.futurecdn.net/nNDbSZ7yaF5BEeE7ftqJV7-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Unsurprisingly, the Bravia 7 II’s ability to drive pure red, green, and blue light straight to the screen — without relying on any white backlight boost — made “Only God Forgives” look exactly the way I imagine the director and his cinematographer intended. While it’s still very much a dark film, the red highlights looked vibrant and well‑defined, without any noticeable bleed into faces or surrounding objects when viewed in person (that nuance doesn’t always translate perfectly in photos — as I mentioned earlier, TVs this bright and saturated can be surprisingly tricky to capture accurately).</p><p>In the end, the Bravia 7 II handled the darkest, most demanding scenes even better than I expected. It kept deep contrast intact while still pulling out the small bits of shadow detail that usually disappear on other sets, all without brightening the image in a way that would undermine what the filmmakers were going for. And with its True RGB backlight system doing the heavy lifting, the Bravia 7 II comes across as a strong option for anyone who cares about accurate, well‑controlled dark‑scene performance — one that gets about as close to a reference‑monitor experience as the consumer TV space currently allows.</p><div data-widget-type="review" data-model-name="Sony Bravia 7 II"></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/tvs/we-just-tested-the-bravia-7-ii-heres-why-i-think-it-could-be-the-sony-tv-to-buy-in-2026">We just tested the Bravia 7 II — here’s why I think it could be the Sony TV to buy in 2026</a></li><li><a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs#section-sony-bravia-9-ii-bravia-7-ii-price-and-availability">I saw Sony's Bravia 9 II up close and it completely shattered my expectations — here’s why it’s the king of RGB TVs</a></li><li><a href="https://www.tomsguide.com/tvs/samsungs-new-micro-rgb-tv-is-finally-here-for-usd3-200-but-id-rather-buy-this-oled-instead">Samsung's new Micro RGB TV is finally here — but I'd rather buy this OLED instead</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/i-stress-tested-sonys-bravia-7-ii-true-rgb-tv-with-the-darkest-scenes-i-could-find-heres-what-happened</link>
                                                                            <description>
                            <![CDATA[ Sony's True RGB display tech makes plenty of promises, so I decided to throw the darkest, most difficult scenes I could find at it to see how it handles them. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">C8mfcuHqvxrq9TfZcmxbEX</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/5tPHvyuZ45bg5ZN2BKSRej-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 06 Jul 2026 01:41:47 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[TVs]]></category>
                                                                                                                    <dc:creator><![CDATA[ Stephen Lambrechts ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/JN3jEQpqyyt32as8AJJqXe-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Stephen Lambrechts is the Managing Editor of Tom&#039;s Guide AU and has written professionally across the categories of tech, film, television and gaming for the last 15 years. Before Tom&#039;s Guide, he spent several years as a Senior Journalist at TechRadar, had a brief stint as Editor in Chief at Official Xbox Magazine Australia, and worked at IGN. He&#039;s also written for such mags and sites as APC, TechLife Australia, T3, FilmInk, Daily Telegraph and AskMen. Additionally, he&#039;s a Tomatometer-approved critic on Rotten Tomatoes, and has offered his tech advice on radio and television.&lt;/p&gt;&lt;p&gt;He&#039;s an expert when it comes to smartphones, TVs, gaming and streaming, having contributed many reviews in each of those categories over the years. Despite his many articles on streaming, he&#039;s a passionate collector of physical media who enjoys listening to vinyl records and acquiring obscure horror movies from the &#039;60s, &#039;70s and 80s. He can usually be seen rocking the latest retro sneaker releases, particularly Air Jordans and Reebok Pumps. Occasionally, he also indulges in other non-hipster stuff, like hiking.&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/5tPHvyuZ45bg5ZN2BKSRej-1920-80.jpg">
                                                            <media:credit><![CDATA[Sony]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Lifestyle image of Sony Bravia 7 II True RGB TV in living room environment ]]></media:description>                                                            <media:text><![CDATA[Lifestyle image of Sony Bravia 7 II True RGB TV in living room environment ]]></media:text>
                                <media:title type="plain"><![CDATA[Lifestyle image of Sony Bravia 7 II True RGB TV in living room environment ]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/5tPHvyuZ45bg5ZN2BKSRej-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>After a week of testing it with the darkest scenes I could find, the Sony Bravia 7 II True RGB TV made me reconsider what today’s TVs are actually capable of in low light. I lined up the usual stress‑test material — everything from “The Batman” to “House of the Dragon” — and watched it alongside my Samsung Neo QLED TV to see how each set handled the same challenges. I wasn’t trying to crown a winner right away; I just wanted to see how differently two modern TVs approach the same problem.</p><p>In our <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Bravia 7 II review</a>, we praised the TV for its ability to deliver "really deep blacks" alongside "precisely managed highlights", but its handling of contrast goes deeper than that — what stood out to me was how the Bravia 7 II's True RGB backlight system treated near‑black detail. </p><p>Even before digging into side‑by‑side comparisons, it was clear Sony’s Bravia 7 II had a distinct way of presenting dark scenes that didn’t rely on simply brightening them. That contrast with the Mini LED-backed Samsung TV became more noticeable the longer I watched.</p><p>The main takeaway is that the Bravia 7 II doesn’t try to reshape dark scenes to make them easier to see — it leans into how they were graded. Sometimes that means a scene stays intentionally dim; other times it reveals details that are easy to miss on other sets. Taking into account how difficult it is to accurately capture the colour and brightness of all RGB TVs in photos, here's how the Bravia 7 II handled the darkest scenes I could throw at it.</p><h2 id="house-of-the-dragon">‘House of the Dragon’</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="YRVD5TruoDjvJpuNtsMoFV" name="7547" alt="House of the Dragon viewed on Sony Bravia 7 II" src="https://cdn.mos.cms.futurecdn.net/YRVD5TruoDjvJpuNtsMoFV-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Like many people who watched the first season of HBO’s “House of the Dragon”, I was incredibly frustrated by one episode that was almost impossible to sit through. I’m talking about episode seven, “Driftmark” — the one filmed and graded so darkly that I could barely tell what was happening on screen, even while watching on Samsung’s flagship at the time, the QN900A Neo QLED 8K TV. </p><p>In his <a href="https://www.youtube.com/watch?v=D83SXcguwBU" target="_blank">testing</a>, Vincent Teoh from the YouTube channel <a href="https://www.youtube.com/@hdtvtest" target="_blank">HDTVTest</a> revealed that some sections of the episode “barely went above 1 nit” of brightness, so it’s no surprise that my television’s backlighting system went haywire in an attempt to compensate for the darkness on display, resulting in extreme haloing and light bloom. The episode also reportedly caused many OLED TVs to auto-dim until the image was almost totally black.</p><p>The backlash from viewers was so intense that showrunner Ryan Condal would eventually comment on the matter, telling <a href="https://variety.com/2022/tv/news/house-of-the-dragon-finale-explained-aemond-luke-rhaenrya-daemon-season-2-1235412136/" target="_blank">Variety</a> that future seasons would “take much more into account the fact that we are making the show for people’s television sets versus in a perfectly calibrated movie theater environment.” In other words, the viewing experience was less than ideal for those of us without super expensive reference monitors.</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="8buSXg3YSZFfjkJrVrs8Mo" name="7546" alt="House of the Dragon viewed on Sony Bravia 7 II" src="https://cdn.mos.cms.futurecdn.net/8buSXg3YSZFfjkJrVrs8Mo-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Naturally, this particular episode was my first stop in testing the Bravia 7 II’s handling of dark scenes. Taking into consideration Sony’s claims that its new True RGB TV range is factory-calibrated, I went in expecting the Bravia 7 II to do a decent job. Sure enough, I was pleased by how I could now make out what was happening in one of the most problematic scenes, which features two characters walking and talking on a beach. While the scene was clearly filmed day for night and then graded down for the sake of additional darkness, shadow detail was now much more visible, and I was able to see the characters’ faces without much issue. Don’t get me wrong — it’s still a very dark scene, but the murkiness and overcompensating backlight from my previous experience was gone.</p><p>The Bravia 7 II’s exceptional tone‑mapping also deserves credit here. Sony’s XR processor does a good job of preserving what the filmmakers actually meant you to see, especially in tougher HDR scenes. Instead of letting deep shadows swallow everything, it balances the contrast just enough so textures in faces or the outline of a creature in a night shot remain visible without brightening the whole frame.</p><h2 id="the-batman">‘The Batman’</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="fGrGVamNQsUXbKXqpSxR36" name="7548" alt="The Batman displayed on Sony Bravia 7 II" src="https://cdn.mos.cms.futurecdn.net/fGrGVamNQsUXbKXqpSxR36-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I absolutely loved “The Batman” when I saw it in theatres, and was excited to watch it again once it hit 4K Ultra HD Blu-ray. While my current Samsung QN900C Neo QLED 8K TV did a decent job displaying most of the film, it did struggle with some of its darker scenes. The opening train station brawl, in particular, was one that caused my TV to raise the brightness until blacks became somewhat grey and washed out.</p><p>Predictably, the Bravia 7 II handled this scene far better, revealing far more shadow detail without dulling the film’s contrast‑heavy mood. The moment when Batman slowly emerges from the darkness to confront a group of thugs has always been a trouble spot on the aforementioned Samsung TV, yet here it finally looked as dark and dramatic as I imagine the filmmakers intended. </p><p>It’s likely that Sony’s XR Backlight Master Drive, which uses a proprietary dimming algorithm, played a big part in keeping “The Batman” moody and dark without letting the shadows turn into a murky blur. The automatic switch to Dolby Vision Bright could also have helped, lifting just enough detail out of the deepest parts of the frame without noticeably changing the film’s overall tone.</p><h2 id="only-god-forgives">‘Only God Forgives’</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="W8GAPp22uqPsoAqWpZB73C" name="7556" alt="Only God Forgives displayed on Sony Bravia 7 II" src="https://cdn.mos.cms.futurecdn.net/W8GAPp22uqPsoAqWpZB73C-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Next, I moved to a film that’s notoriously tough on both OLED and Mini‑LED displays: “Only God Forgives”, from director Nicolas Winding Refn. Its relentless swings between pitch‑black shadows and hyper‑saturated neon highlights often expose local‑dimming delays, where the backlight or pixels can’t react quickly enough, creating faint ghosting that pulls you out of the moment.</p><p>The film’s heavy use of harsh red lighting against deep darkness also pushes the red channel hard, sometimes leading to clipping that bleeds into surrounding objects and wipes out fine detail along edges and contours.</p><p>A big part of why these issues show up is simply how most TVs generate brightness in the first place. Mini‑LED sets rely on a white backlight, and WRGB OLEDs boost luminance with a white subpixel. When a scene suddenly demands intense, saturated colour — especially neon red — the TV often leans on that white source to hit the required brightness. In a film this stylised, that can wash out the purity of the colour, lift nearby blacks, and make edges look a little less clean than they should.</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="nNDbSZ7yaF5BEeE7ftqJV7" name="7554" alt="Only God Forgives displayed on Sony Bravia 7 II" src="https://cdn.mos.cms.futurecdn.net/nNDbSZ7yaF5BEeE7ftqJV7-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Unsurprisingly, the Bravia 7 II’s ability to drive pure red, green, and blue light straight to the screen — without relying on any white backlight boost — made “Only God Forgives” look exactly the way I imagine the director and his cinematographer intended. While it’s still very much a dark film, the red highlights looked vibrant and well‑defined, without any noticeable bleed into faces or surrounding objects when viewed in person (that nuance doesn’t always translate perfectly in photos — as I mentioned earlier, TVs this bright and saturated can be surprisingly tricky to capture accurately).</p><p>In the end, the Bravia 7 II handled the darkest, most demanding scenes even better than I expected. It kept deep contrast intact while still pulling out the small bits of shadow detail that usually disappear on other sets, all without brightening the image in a way that would undermine what the filmmakers were going for. And with its True RGB backlight system doing the heavy lifting, the Bravia 7 II comes across as a strong option for anyone who cares about accurate, well‑controlled dark‑scene performance — one that gets about as close to a reference‑monitor experience as the consumer TV space currently allows.</p><div data-widget-type="review" data-model-name="Sony Bravia 7 II"></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/tvs/we-just-tested-the-bravia-7-ii-heres-why-i-think-it-could-be-the-sony-tv-to-buy-in-2026">We just tested the Bravia 7 II — here’s why I think it could be the Sony TV to buy in 2026</a></li><li><a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs#section-sony-bravia-9-ii-bravia-7-ii-price-and-availability">I saw Sony's Bravia 9 II up close and it completely shattered my expectations — here’s why it’s the king of RGB TVs</a></li><li><a href="https://www.tomsguide.com/tvs/samsungs-new-micro-rgb-tv-is-finally-here-for-usd3-200-but-id-rather-buy-this-oled-instead">Samsung's new Micro RGB TV is finally here — but I'd rather buy this OLED instead</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Target's massive 4th of July sale cuts up to $600 off cameras — big savings on Canon, Nikon and more ]]></title>
                                                                                                <dc:content><![CDATA[ <p>As we get ever closer to the 4th of July, the big sales keep on coming. Yesterday I covered the best <a href="https://www.tomsguide.com/tech/18-best-target-4th-of-july-tech-deals-save-big-on-headphones-laptops-and-wearables?hasComeFromProof=true" target="_blank">Target tech deals</a> and now I'm looking at the retailer's enviable selection of cameras.</p><p>If you're looking to step up from taking snaps on your smartphone, then you'll want to at least consider a DSLR. I've picked out some top models from Canon, Nikon and Sony here, across a range of different price points.</p><p>Alternatively, you might be after something much simpler. If so, I've also selected a few cool instant cameras that are just the thing for taking on your summer trips.  </p><p>There are lots more things to buy in the <a href="https://www.tomsguide.com/sales-events/best-4th-of-july-sales-2026-walmart-home-depot-more">4th of July sales</a>. If you're looking for more deals then check out our guides to <a href="https://www.tomsguide.com/home/home-appliances/best-buy-4th-of-july-sale-huge-appliance-discounts-from-ge-lg-and-samsung-save-up-to-usd1560" target="_blank">Best Buy's appliance sale</a>, as well as its <a href="https://www.tomsguide.com/tvs/4th-of-july-tv-deals-save-up-to-usd1-700-on-lg-samsung-and-more-at-best-buy" target="_blank">TV</a> and <a href="https://www.tomsguide.com/audio/bluetooth-speakers/best-buy-4th-of-july-sale-score-up-to-33-percent-off-these-9-bluetooth-speakers-from-sony-klipsch-jbl-and-more" target="_blank">audio sales </a>too.</p>        <div class="featured_product_block featured_block_hero" data-id="7a28519a-b1b6-4e03-bbb0-24d7e774332d">            <a href="https://www.target.com/p/kodak-smile-classic-digital-instant-camera-for-3-5-x-4-25-zink-photo-paper-bluetooth-16mp-pictures/-/A-82282261?preselect=82282260#lnk=sametab" data-model-name="Kodak Smile Classic Digital Instant Camera" 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/AC8Vs4gfNxRzcMWKee7958.webp" alt="Kodak Smile Classic Digital Instant Camera for 3.5 X 4.25 Zink Photo Paper - Bluetooth, 16mp Pictures (red)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Kodak</div>                                        <div class="featured__title">Kodak Smile Classic Digital Instant Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="57ca64da-74eb-421e-876a-72633ae005f7">            <a href="https://www.target.com/p/kodak-pixpro-fz55-digital-camera-red-32gb-memory-card-camera-case/-/A-1006353493#lnk=sametab" data-model-name="Kodak PIXPRO FZ55 Camera with 32GB Memory Card & Camera Case" 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/GHmzZL8oME8Zn75RW7dEH4.webp" alt="Kodak Pixpro Fz55 Digital Camera Red With 32gb Memory Card & Camera Case"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Kodak</div>                                        <div class="featured__title">Kodak PIXPRO FZ55 Camera with 32GB Memory Card & Camera Case</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="f6205ebf-d2cc-4a4b-91b4-c76c63f8699c">            <a href="https://www.target.com/p/fujifilm-instax-mini-12-camera-blossom-pink-case-instax-mini-instant-film-100-sheets-mini-photo-album/-/A-1007330499#lnk=sametab" data-model-name="Fujifilm Instax Mini 12 Camera with Instant Film and Photo Album" 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/umzweWZ2AXFxdD9cUVwnd7.webp" alt="Fujifilm Instax Mini 12 Camera (blossom Pink)   Case   Instax Mini Instant Film (100 Sheets)   Mini Photo Album"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Fujifilm</div>                                        <div class="featured__title">Fujifilm Instax Mini 12 Camera with Instant Film and Photo Album</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="12681ba5-42de-4eef-8233-f0a0573613d4">            <a href="https://www.target.com/p/canon-eos-r100-rf-s18-45mm-f4-5-6-3-is-stm-lens-kit/-/A-89177813#lnk=sametab" data-model-name="Canon EOS R100 Mirrorless Camera" 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/kJtFV92p9aVnXbRg7gFNdR.webp" alt="Canon Eos R100 Rf-S18-45mm F4.5-6.3 Is Stm Lens Kit: 24.1mp Mirrorless, Wi-Fi"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">Canon EOS R100 Mirrorless Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="d29abe2e-8f7f-4c0b-a05d-1be82ae1bdf2">            <a href="https://www.target.com/p/om-system-om-d-e-m10-mark-iv-black-mirrorless-camera-with-14-42mm-ez-lens-kit/-/A-1000985462#lnk=sametab" data-model-name="OM System OM-D E-M10 Mark IV Mirrorless Camera with 14-42mm EZ Lens Kit" 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/ebsNY4SqyPTBU7fJtrgQg9.webp" alt="Om System Om-D E-M10 Mark Iv Black Mirrorless Camera With 14-42mm Ez Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>OM System</div>                                        <div class="featured__title">OM System OM-D E-M10 Mark IV Mirrorless Camera with 14-42mm EZ Lens Kit</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="0bc625ae-d059-469e-8714-95d2da4ffe7f">            <a href="https://www.target.com/p/canon-eos-r50-rf-s18-45mm-f4-5-6-3mm-is-stm-kit/-/A-88661030#lnk=sametab" data-model-name="Canon EOS R50 Mirrorless Digital Camera" 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/UFvqDWznhiVZNcWmoUNBe6.webp" alt="Canon Eos R50 Rf-S18-45mm F4.5-6.3mm Is Stm Kit: Mirrorless Wi-Fi Digital Camera"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">Canon EOS R50 Mirrorless Digital Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="3e94430b-90b1-400d-9a7b-1beb8d3a9c35">            <a href="https://www.target.com/p/sony-alpha-zv-e10-mirrorless-vlog-camera-with-16-50mm-f-3-5-5-6-ii-lens-black/-/A-1009791818#lnk=sametab" data-model-name="Sony Alpha ZV-E10 Mirrorless Camera with 16-50mm Lens" 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/jbohhK55NQvx38erhntf9S.webp" alt="Sony Alpha Zv-E10 Mirrorless Vlog Camera With 16-50mm F/3.5-5.6 Ii Lens (black)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Sony</div>                                        <div class="featured__title">Sony Alpha ZV-E10 Mirrorless Camera with 16-50mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="fb05059f-7f53-4390-9a9d-4917736042c8">            <a href="https://www.target.com/p/canon-eos-rp-mirrorless-digital-camera-with-24-105mm-f-4-7-1-stm-lens-kit/-/A-83999286#lnk=sametab" data-model-name="Canon EOS RP Mirrorless Digital Camera with 24-105mm Lens" 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/PRYXSrS7ZXnrSfAWLSCmxW.webp" alt="Canon Eos Rp Mirrorless Digital Camera With 24-105mm F/4-7.1 Stm Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Canon</div>                                        <div class="featured__title">Canon EOS RP Mirrorless Digital Camera with 24-105mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1c170288-f19c-4b5b-9d91-191f36bc6c0a">            <a href="https://www.target.com/p/om-system-om-5-mark-ii-black-mirrorless-camera-with-14-150mm-lens-kit/-/A-1006521726#lnk=sametab" data-model-name="Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit" 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/rH274JEp4qE3KVmz4ziDx8.webp" alt="Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all OM System</div>                                        <div class="featured__title">Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9b14d8be-01ff-4767-93c7-74d9b58ce0ff">            <a href="https://www.target.com/p/sony-alpha-zve10-ii-aps-c-interchangeable-lens-mirrorless-designed-for-content-creators-body-only-black/-/A-84244533#lnk=sametab" data-model-name="Sony Alpha A7 III Mirrorless Camera with 28-70mm Lens" 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/tyKydjCtF6TBXUuSpLiQVS.webp" alt="Sony Alpha A7 Iii 24.2mp Full Frame Mirrorless Digital Camera With 28-70mm Lens"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Sony Alpha A7 III Mirrorless Camera with 28-70mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="e1779bc6-9c01-4a08-bfea-d89cf9a20d98">            <a href="https://www.target.com/p/nikon-z5-ii-mirrorless-digital-camera-with-z-24-200mm-f-4-6-3-vr-lens/-/A-1003487515#lnk=sametab" data-model-name="Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens" 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/Nahbx7MHmvTqeuhDcVk9s8.webp" alt="Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Nikon</div>                                        <div class="featured__title">Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="a0e6535a-2b3d-40ee-8226-29b8f030c5f1">            <a href="https://www.target.com/p/nikon-zf-mirrorless-camera-with-24-70mm-f-4-lens-24-5mp-4k-uhd-video-silver/-/A-1007130727#lnk=sametab" data-model-name="Nikon Zf Mirrorless Camera with 24-70mm F/4 Lens" 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/GLn79FhaFACLS65qjRv877.webp" alt="Nikon Zf Mirrorless Camera With 24-70mm F/4 Lens - 24.5mp, 4k Uhd Video (silver)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nikon</div>                                        <div class="featured__title">Nikon Zf Mirrorless Camera with 24-70mm F/4 Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/targets-massive-4th-of-july-sale-cuts-up-to-usd600-off-cameras-big-savings-on-canon-nikon-and-more</link>
                                                                            <description>
                            <![CDATA[ Target's 4th of July sale has slashed prices on a range of cameras, from feature-packed DSLR models to cool instant snappers. Check out my 12 favorite bargains. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">3bYZwuJHUKiLheiGp8BLKd</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/gyY9fdEWMLHEigw3b4g8L7-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 03 Jul 2026 16:26:27 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras & Photography]]></category>
                                                                                                                    <dc:creator><![CDATA[ Will Salmon ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/HpRWP7Mh9ErgRGoTvZBbBJ-320-70.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/gyY9fdEWMLHEigw3b4g8L7-1920-80.jpg">
                                                            <media:credit><![CDATA[Nikon, Fujifilm, Kodak, Sony, Canon / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of cameras on a cream background.]]></media:description>                                                            <media:text><![CDATA[A selection of cameras on a cream background.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of cameras on a cream background.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/gyY9fdEWMLHEigw3b4g8L7-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As we get ever closer to the 4th of July, the big sales keep on coming. Yesterday I covered the best <a href="https://www.tomsguide.com/tech/18-best-target-4th-of-july-tech-deals-save-big-on-headphones-laptops-and-wearables?hasComeFromProof=true" target="_blank">Target tech deals</a> and now I'm looking at the retailer's enviable selection of cameras.</p><p>If you're looking to step up from taking snaps on your smartphone, then you'll want to at least consider a DSLR. I've picked out some top models from Canon, Nikon and Sony here, across a range of different price points.</p><p>Alternatively, you might be after something much simpler. If so, I've also selected a few cool instant cameras that are just the thing for taking on your summer trips.  </p><p>There are lots more things to buy in the <a href="https://www.tomsguide.com/sales-events/best-4th-of-july-sales-2026-walmart-home-depot-more">4th of July sales</a>. If you're looking for more deals then check out our guides to <a href="https://www.tomsguide.com/home/home-appliances/best-buy-4th-of-july-sale-huge-appliance-discounts-from-ge-lg-and-samsung-save-up-to-usd1560" target="_blank">Best Buy's appliance sale</a>, as well as its <a href="https://www.tomsguide.com/tvs/4th-of-july-tv-deals-save-up-to-usd1-700-on-lg-samsung-and-more-at-best-buy" target="_blank">TV</a> and <a href="https://www.tomsguide.com/audio/bluetooth-speakers/best-buy-4th-of-july-sale-score-up-to-33-percent-off-these-9-bluetooth-speakers-from-sony-klipsch-jbl-and-more" target="_blank">audio sales </a>too.</p>        <div class="featured_product_block featured_block_hero" data-id="7a28519a-b1b6-4e03-bbb0-24d7e774332d">            <a href="https://www.target.com/p/kodak-smile-classic-digital-instant-camera-for-3-5-x-4-25-zink-photo-paper-bluetooth-16mp-pictures/-/A-82282261?preselect=82282260#lnk=sametab" data-model-name="Kodak Smile Classic Digital Instant Camera" 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/AC8Vs4gfNxRzcMWKee7958.webp" alt="Kodak Smile Classic Digital Instant Camera for 3.5 X 4.25 Zink Photo Paper - Bluetooth, 16mp Pictures (red)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Kodak</div>                                        <div class="featured__title">Kodak Smile Classic Digital Instant Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="57ca64da-74eb-421e-876a-72633ae005f7">            <a href="https://www.target.com/p/kodak-pixpro-fz55-digital-camera-red-32gb-memory-card-camera-case/-/A-1006353493#lnk=sametab" data-model-name="Kodak PIXPRO FZ55 Camera with 32GB Memory Card & Camera Case" 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/GHmzZL8oME8Zn75RW7dEH4.webp" alt="Kodak Pixpro Fz55 Digital Camera Red With 32gb Memory Card & Camera Case"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Kodak</div>                                        <div class="featured__title">Kodak PIXPRO FZ55 Camera with 32GB Memory Card & Camera Case</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="f6205ebf-d2cc-4a4b-91b4-c76c63f8699c">            <a href="https://www.target.com/p/fujifilm-instax-mini-12-camera-blossom-pink-case-instax-mini-instant-film-100-sheets-mini-photo-album/-/A-1007330499#lnk=sametab" data-model-name="Fujifilm Instax Mini 12 Camera with Instant Film and Photo Album" 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/umzweWZ2AXFxdD9cUVwnd7.webp" alt="Fujifilm Instax Mini 12 Camera (blossom Pink)   Case   Instax Mini Instant Film (100 Sheets)   Mini Photo Album"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Fujifilm</div>                                        <div class="featured__title">Fujifilm Instax Mini 12 Camera with Instant Film and Photo Album</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="12681ba5-42de-4eef-8233-f0a0573613d4">            <a href="https://www.target.com/p/canon-eos-r100-rf-s18-45mm-f4-5-6-3-is-stm-lens-kit/-/A-89177813#lnk=sametab" data-model-name="Canon EOS R100 Mirrorless Camera" 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/kJtFV92p9aVnXbRg7gFNdR.webp" alt="Canon Eos R100 Rf-S18-45mm F4.5-6.3 Is Stm Lens Kit: 24.1mp Mirrorless, Wi-Fi"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">Canon EOS R100 Mirrorless Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="d29abe2e-8f7f-4c0b-a05d-1be82ae1bdf2">            <a href="https://www.target.com/p/om-system-om-d-e-m10-mark-iv-black-mirrorless-camera-with-14-42mm-ez-lens-kit/-/A-1000985462#lnk=sametab" data-model-name="OM System OM-D E-M10 Mark IV Mirrorless Camera with 14-42mm EZ Lens Kit" 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/ebsNY4SqyPTBU7fJtrgQg9.webp" alt="Om System Om-D E-M10 Mark Iv Black Mirrorless Camera With 14-42mm Ez Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>OM System</div>                                        <div class="featured__title">OM System OM-D E-M10 Mark IV Mirrorless Camera with 14-42mm EZ Lens Kit</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="0bc625ae-d059-469e-8714-95d2da4ffe7f">            <a href="https://www.target.com/p/canon-eos-r50-rf-s18-45mm-f4-5-6-3mm-is-stm-kit/-/A-88661030#lnk=sametab" data-model-name="Canon EOS R50 Mirrorless Digital Camera" 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/UFvqDWznhiVZNcWmoUNBe6.webp" alt="Canon Eos R50 Rf-S18-45mm F4.5-6.3mm Is Stm Kit: Mirrorless Wi-Fi Digital Camera"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">Canon EOS R50 Mirrorless Digital Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="3e94430b-90b1-400d-9a7b-1beb8d3a9c35">            <a href="https://www.target.com/p/sony-alpha-zv-e10-mirrorless-vlog-camera-with-16-50mm-f-3-5-5-6-ii-lens-black/-/A-1009791818#lnk=sametab" data-model-name="Sony Alpha ZV-E10 Mirrorless Camera with 16-50mm Lens" 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/jbohhK55NQvx38erhntf9S.webp" alt="Sony Alpha Zv-E10 Mirrorless Vlog Camera With 16-50mm F/3.5-5.6 Ii Lens (black)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Sony</div>                                        <div class="featured__title">Sony Alpha ZV-E10 Mirrorless Camera with 16-50mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="fb05059f-7f53-4390-9a9d-4917736042c8">            <a href="https://www.target.com/p/canon-eos-rp-mirrorless-digital-camera-with-24-105mm-f-4-7-1-stm-lens-kit/-/A-83999286#lnk=sametab" data-model-name="Canon EOS RP Mirrorless Digital Camera with 24-105mm Lens" 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/PRYXSrS7ZXnrSfAWLSCmxW.webp" alt="Canon Eos Rp Mirrorless Digital Camera With 24-105mm F/4-7.1 Stm Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Canon</div>                                        <div class="featured__title">Canon EOS RP Mirrorless Digital Camera with 24-105mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1c170288-f19c-4b5b-9d91-191f36bc6c0a">            <a href="https://www.target.com/p/om-system-om-5-mark-ii-black-mirrorless-camera-with-14-150mm-lens-kit/-/A-1006521726#lnk=sametab" data-model-name="Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit" 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/rH274JEp4qE3KVmz4ziDx8.webp" alt="Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all OM System</div>                                        <div class="featured__title">Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9b14d8be-01ff-4767-93c7-74d9b58ce0ff">            <a href="https://www.target.com/p/sony-alpha-zve10-ii-aps-c-interchangeable-lens-mirrorless-designed-for-content-creators-body-only-black/-/A-84244533#lnk=sametab" data-model-name="Sony Alpha A7 III Mirrorless Camera with 28-70mm Lens" 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/tyKydjCtF6TBXUuSpLiQVS.webp" alt="Sony Alpha A7 Iii 24.2mp Full Frame Mirrorless Digital Camera With 28-70mm Lens"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Sony Alpha A7 III Mirrorless Camera with 28-70mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="e1779bc6-9c01-4a08-bfea-d89cf9a20d98">            <a href="https://www.target.com/p/nikon-z5-ii-mirrorless-digital-camera-with-z-24-200mm-f-4-6-3-vr-lens/-/A-1003487515#lnk=sametab" data-model-name="Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens" 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/Nahbx7MHmvTqeuhDcVk9s8.webp" alt="Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Nikon</div>                                        <div class="featured__title">Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="a0e6535a-2b3d-40ee-8226-29b8f030c5f1">            <a href="https://www.target.com/p/nikon-zf-mirrorless-camera-with-24-70mm-f-4-lens-24-5mp-4k-uhd-video-silver/-/A-1007130727#lnk=sametab" data-model-name="Nikon Zf Mirrorless Camera with 24-70mm F/4 Lens" 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/GLn79FhaFACLS65qjRv877.webp" alt="Nikon Zf Mirrorless Camera With 24-70mm F/4 Lens - 24.5mp, 4k Uhd Video (silver)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nikon</div>                                        <div class="featured__title">Nikon Zf Mirrorless Camera with 24-70mm F/4 Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Best Buy 4th of July sale: Score up to 33% off these 9 Bluetooth speakers from Sony, Klipsch, JBL, and more ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The deals just keep coming! Yes, Amazon's Prime Day sales may be over, but Best Buy is already running a huge 4th of July sale that's offering lots of great bargains. We covered the <a href="https://www.tomsguide.com/tvs/4th-of-july-tv-deals-save-up-to-usd1-700-on-lg-samsung-and-more-at-best-buy">Best Buy TV sale</a> earlier and now we're rounding up the best Bluetooth speakers, which you can get right now with some pretty deep discounts.</p><p>Bluetooth speakers come in all shapes and sizes and have a variety of price points to match. We've started our selection with some good value Soundcore and Marshall offerings that are ideal for taking to the beach or lounging by the pool. We've then turned our attention to some feature-packed offerings from the likes of JBL before rounding out with some seriously high-end speakers from the likes of Sony and Klipsch. </p><p>For more amazing deals, make sure to check out our guide to the best <a href="https://www.tomsguide.com/sales-events/best-4th-of-july-sales-2026-walmart-home-depot-more">4th of July sales</a>.</p>        <div class="featured_product_block featured_block_hero" data-id="712f8dac-c812-4827-ac4f-3e4e108c1cbc">            <a href="https://www.bestbuy.com/product/soundcore-by-anker-boom-2-ipx7-waterproof-floatable-80w-bluetooth-speaker-with-subwoofer-bassup-2-0-custom-rgb-light-effects-black/JJ858R2J8J" data-model-name="Soundcore by Anker Boom 2 Waterproof Floatable Bluetooth Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/SFBcdMYYSfhFFS4dFidjba.jpg" alt="Soundcore by Anker Boom 2 Waterproof Floatable Bluetooth Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Soundcore</div>                                        <div class="featured__title">Soundcore by Anker Boom 2 Waterproof Floatable Bluetooth Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="72e8b1f0-9065-4207-897c-b2786e91ab90">            <a href="https://www.bestbuy.com/product/marshall-emberton-iii-portable-bluetooth-speaker-with-rugged-waterproof-dust-proof-wireless-design-black-brass/CFCR4HZWQ3" data-model-name="Marshall Emberton III Portable Bluetooth Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/pTDwusmBCM8F6nQVfBS4kj.jpg" alt="Marshall Emberton III Portable Bluetooth Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Marshall</div>                                        <div class="featured__title">Marshall Emberton III Portable Bluetooth Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="da87ce23-2726-405d-817b-0d461f206737">            <a href="https://www.bestbuy.com/product/jbl-charge-6-portable-wireless-speaker-2025-black/J7LXFW3J5H" data-model-name="JBL Charge 6 Portable Wireless Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/fNc3HowqkmrigZci7YeZP9.jpg" alt="JBL Charge 6 Portable Wireless Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>JBL</div>                                        <div class="featured__title">JBL Charge 6 Portable Wireless Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="6394d793-4f9d-4169-b8e9-f4287d157704">            <a href="https://www.bestbuy.com/product/sony-ult-field-7-wireless-speaker-black/J7XSRH5SLS" data-model-name="Sony ULT Field 7 Wireless Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jcBiuei57zrGSDzAbcJr2S.jpg" alt="Sony ULT Field 7 Wireless Speaker."></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Sony ULT Field 7 Wireless Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="0fd1810e-fa9f-4d2b-b99e-69fd694e6858">            <a href="https://www.bestbuy.com/product/klipsch-miami-music-city-bluetooth-party-speaker-black/JX44FC55Y2" data-model-name="Klipsch Miami Music City Bluetooth Party Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/BzW3r6PT8QwtpdanA5diAW.jpg" alt="Klipsch Miami Music City Bluetooth Party Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Klipsch</div>                                        <div class="featured__title">Klipsch Miami Music City Bluetooth Party Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="37fe2c4e-9a04-4d51-a317-dbe53dc403cc">            <a href="https://www.bestbuy.com/product/jbl-party-box-710-portable-party-speaker-2024-black/J7LXFW26JQ" data-model-name="JBL Party Box 710 Party Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Z5YFfJTwNSdswvDE4HJVth.jpg" alt="JBL Party Box 710 Party Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>JBL</div>                                        <div class="featured__title">JBL Party Box 710 Party Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="790534ee-31fc-415c-9f5c-cd0a02a81e31">            <a href="https://www.bestbuy.com/product/sony-ult-tower-9-wireless-party-speaker-srsult900-black/J7XSRH5HJF" data-model-name="Sony ULT Tower 9 Party Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/2Jg8KepmdYjZu8bGsVDoJn.jpg" alt="Sony ULT Tower 9 Party Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Sony ULT Tower 9 Party Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="fd814dd7-86e9-4a5f-88c0-ee3097d40a6c">            <a href="https://www.bestbuy.com/product/klipsch-new-york-music-city-bluetooth-party-speaker-black/JX44FC55SQ" data-model-name="Klipsch New York Music City Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/tdvFuabkWw8cNh4ViovHHT.jpg" alt="Klipsch New York Music City Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Klipsch</div>                                        <div class="featured__title">Klipsch New York Music City Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8ca1621c-d3c3-4cfa-9c37-04fa3939224e">            <a href="https://www.bestbuy.com/product/sony-ult-tower-10-party-speaker-with-wireless-microphone-black/J7XSRH5SY2" data-model-name="Sony ULT Tower 10 Party Speaker with Wireless Microphone" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ccNEpLjSb4ZqGBPTSnBm5c.jpg" alt="Sony ULT Tower 10 Party Speaker with Wireless Microphone"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Sony ULT Tower 10 Party Speaker with Wireless Microphone</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/bluetooth-speakers/best-buy-4th-of-july-sale-score-up-to-33-percent-off-these-9-bluetooth-speakers-from-sony-klipsch-jbl-and-more</link>
                                                                            <description>
                            <![CDATA[ Get the party started with one of these seriously good-sounding Bluetooth speakers – all now with healthy discounts. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">JPMnAfETWjyzJNKAaJBqbD</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/4838biTYLTZN6JA3uoH52E-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 30 Jun 2026 19:17:08 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Bluetooth Speakers]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Speakers]]></category>
                                                                                                                    <dc:creator><![CDATA[ Will Salmon ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/HpRWP7Mh9ErgRGoTvZBbBJ-320-70.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/4838biTYLTZN6JA3uoH52E-1920-80.jpg">
                                                            <media:credit><![CDATA[Sony, Anker, Klipsch, JBL / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of Bluetooth speakers.]]></media:description>                                                            <media:text><![CDATA[A selection of Bluetooth speakers.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of Bluetooth speakers.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/4838biTYLTZN6JA3uoH52E-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The deals just keep coming! Yes, Amazon's Prime Day sales may be over, but Best Buy is already running a huge 4th of July sale that's offering lots of great bargains. We covered the <a href="https://www.tomsguide.com/tvs/4th-of-july-tv-deals-save-up-to-usd1-700-on-lg-samsung-and-more-at-best-buy">Best Buy TV sale</a> earlier and now we're rounding up the best Bluetooth speakers, which you can get right now with some pretty deep discounts.</p><p>Bluetooth speakers come in all shapes and sizes and have a variety of price points to match. We've started our selection with some good value Soundcore and Marshall offerings that are ideal for taking to the beach or lounging by the pool. We've then turned our attention to some feature-packed offerings from the likes of JBL before rounding out with some seriously high-end speakers from the likes of Sony and Klipsch. </p><p>For more amazing deals, make sure to check out our guide to the best <a href="https://www.tomsguide.com/sales-events/best-4th-of-july-sales-2026-walmart-home-depot-more">4th of July sales</a>.</p>        <div class="featured_product_block featured_block_hero" data-id="712f8dac-c812-4827-ac4f-3e4e108c1cbc">            <a href="https://www.bestbuy.com/product/soundcore-by-anker-boom-2-ipx7-waterproof-floatable-80w-bluetooth-speaker-with-subwoofer-bassup-2-0-custom-rgb-light-effects-black/JJ858R2J8J" data-model-name="Soundcore by Anker Boom 2 Waterproof Floatable Bluetooth Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/SFBcdMYYSfhFFS4dFidjba.jpg" alt="Soundcore by Anker Boom 2 Waterproof Floatable Bluetooth Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Soundcore</div>                                        <div class="featured__title">Soundcore by Anker Boom 2 Waterproof Floatable Bluetooth Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="72e8b1f0-9065-4207-897c-b2786e91ab90">            <a href="https://www.bestbuy.com/product/marshall-emberton-iii-portable-bluetooth-speaker-with-rugged-waterproof-dust-proof-wireless-design-black-brass/CFCR4HZWQ3" data-model-name="Marshall Emberton III Portable Bluetooth Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/pTDwusmBCM8F6nQVfBS4kj.jpg" alt="Marshall Emberton III Portable Bluetooth Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Marshall</div>                                        <div class="featured__title">Marshall Emberton III Portable Bluetooth Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="da87ce23-2726-405d-817b-0d461f206737">            <a href="https://www.bestbuy.com/product/jbl-charge-6-portable-wireless-speaker-2025-black/J7LXFW3J5H" data-model-name="JBL Charge 6 Portable Wireless Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/fNc3HowqkmrigZci7YeZP9.jpg" alt="JBL Charge 6 Portable Wireless Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>JBL</div>                                        <div class="featured__title">JBL Charge 6 Portable Wireless Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="6394d793-4f9d-4169-b8e9-f4287d157704">            <a href="https://www.bestbuy.com/product/sony-ult-field-7-wireless-speaker-black/J7XSRH5SLS" data-model-name="Sony ULT Field 7 Wireless Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jcBiuei57zrGSDzAbcJr2S.jpg" alt="Sony ULT Field 7 Wireless Speaker."></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Sony ULT Field 7 Wireless Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="0fd1810e-fa9f-4d2b-b99e-69fd694e6858">            <a href="https://www.bestbuy.com/product/klipsch-miami-music-city-bluetooth-party-speaker-black/JX44FC55Y2" data-model-name="Klipsch Miami Music City Bluetooth Party Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/BzW3r6PT8QwtpdanA5diAW.jpg" alt="Klipsch Miami Music City Bluetooth Party Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Klipsch</div>                                        <div class="featured__title">Klipsch Miami Music City Bluetooth Party Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="37fe2c4e-9a04-4d51-a317-dbe53dc403cc">            <a href="https://www.bestbuy.com/product/jbl-party-box-710-portable-party-speaker-2024-black/J7LXFW26JQ" data-model-name="JBL Party Box 710 Party Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Z5YFfJTwNSdswvDE4HJVth.jpg" alt="JBL Party Box 710 Party Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>JBL</div>                                        <div class="featured__title">JBL Party Box 710 Party Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="790534ee-31fc-415c-9f5c-cd0a02a81e31">            <a href="https://www.bestbuy.com/product/sony-ult-tower-9-wireless-party-speaker-srsult900-black/J7XSRH5HJF" data-model-name="Sony ULT Tower 9 Party Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/2Jg8KepmdYjZu8bGsVDoJn.jpg" alt="Sony ULT Tower 9 Party Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Sony ULT Tower 9 Party Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="fd814dd7-86e9-4a5f-88c0-ee3097d40a6c">            <a href="https://www.bestbuy.com/product/klipsch-new-york-music-city-bluetooth-party-speaker-black/JX44FC55SQ" data-model-name="Klipsch New York Music City Speaker" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/tdvFuabkWw8cNh4ViovHHT.jpg" alt="Klipsch New York Music City Speaker"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Klipsch</div>                                        <div class="featured__title">Klipsch New York Music City Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8ca1621c-d3c3-4cfa-9c37-04fa3939224e">            <a href="https://www.bestbuy.com/product/sony-ult-tower-10-party-speaker-with-wireless-microphone-black/J7XSRH5SY2" data-model-name="Sony ULT Tower 10 Party Speaker with Wireless Microphone" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ccNEpLjSb4ZqGBPTSnBm5c.jpg" alt="Sony ULT Tower 10 Party Speaker with Wireless Microphone"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Sony ULT Tower 10 Party Speaker with Wireless Microphone</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Don’t miss out: these last‑minute EOFY TV deals are your final chance to score a new set at clearance prices ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The end is near for this year’s <a href="https://www.tomsguide.com/deals/eofy-sale-australia">EOFY sales</a>, with the price‑cut frenzy wrapping up as June 30 comes to a close. Even so, there’s still time to snag a last‑minute TV bargain before the new financial year kicks in.</p><p>If you’ve been thinking about upgrading your TV, this is the moment to stop scrolling and start grabbing. Retailers are clearing out stock fast, and the mix of price drops, bonus offers and surprise markdowns means you can pick up a genuinely great screen for far less than you’d expect. </p><p>I’ve seen bargains you wouldn’t believe. Door‑buster TV deals stacked higher than warehouse shelves, flash sales that lit up retailer sites like neon signs in the night. I watched prices plunge in the final hours of EOFY, deeper than anyone thought possible. All of those deals will be lost in time, like tears in rain. Time to buy.</p><p>I cannot stress this enough — this is your last chance to score an epic TV deal at clearance prices before the EOFY sales are over. If you’ve been waiting for the right moment to refresh your home setup with a new Samsung, Sony or Hisense television, you won't find a better time than now — here are my picks for the best last-minute EOFY TV deals.</p><div class="product"><a data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999" href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="xA3gfVDXp63Y6AQJqMGjHi" name="Sony-Bravia-7-II" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/xA3gfVDXp63Y6AQJqMGjHi-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow" data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999">Bravia 7 II True RGB TV (85-inch): was $7499 now $5999</a></strong><br>Sony's new True RGB TV range, which includes the Bravia 7 II, is pushing display technology to new heights thanks to an incredible backlight system that uses individually controlled red, green and blue LEDs to deliver pure colour and brightness to its screen. That means OLED-level contrast and immense colour accuracy at any brightness level. Right now, the Bravia 7 II is currently discounted by up to AU$1,500 on Sony's online store for EOFY. </p><p>50-inch: <del>AU$2,699</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-50xr70m2-az1" target="_blank">AU$2,299</a><br>55-inch: <del>AU$2,999</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-55xr70m2-az1" target="_blank">AU$2,499</a><br>65-inch: <del>AU$3,999</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-65xr70m2-az1" target="_blank">AU$3,299</a><br>75-inch: <del>AU$5,499</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-75xr70m2-az1" target="_blank">AU$4,499</a><br>85-inch: <del>AU$7,499</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank">AU$5,999</a><a class="view-deal button" href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow" data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999">View Deal</a></p></div><div class="product"><a data-dimension112="158599ca-0104-4519-82b2-05d61dc055f5" data-action="Deal Block" data-label="S90H (65-inch)" data-dimension48="S90H (65-inch)" data-dimension25="$2778" href="https://www.thegoodguys.com.au/samsung-65-inches-oled-s90h-4k-smart-ai-tv-2026-qa65s90hawxxy" 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="4WEZaDtsLkVzw5ZzmTd3zX" name="samsung-s90h" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4WEZaDtsLkVzw5ZzmTd3zX-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/samsung-65-inches-oled-s90h-4k-smart-ai-tv-2026-qa65s90hawxxy" target="_blank" rel="nofollow" data-dimension112="158599ca-0104-4519-82b2-05d61dc055f5" data-action="Deal Block" data-label="S90H (65-inch)" data-dimension48="S90H (65-inch)" data-dimension25="$2778">S90H (65-inch): was $3595 now $2778</a></strong><br>I was <a href="https://www.tomsguide.com/tvs/samsungs-2026-australian-tv-lineup-is-packed-with-upgrades-but-i-was-utterly-blown-away-by-one-model-in-particular">blown away</a> by Samsung’s S90H OLED TV when I saw it earlier this year, and this particular model is the one I would pick from Samsung's 2026 lineup. It's basically the same panel as the flagship S95H, minus the polarising metallic frame design, meaning it also boasts a glare-free display. Make sure you click on the 'Price Check' button to get the full discount.<a class="view-deal button" href="https://www.thegoodguys.com.au/samsung-65-inches-oled-s90h-4k-smart-ai-tv-2026-qa65s90hawxxy" target="_blank" rel="nofollow" data-dimension112="158599ca-0104-4519-82b2-05d61dc055f5" data-action="Deal Block" data-label="S90H (65-inch)" data-dimension48="S90H (65-inch)" data-dimension25="$2778">View Deal</a></p></div><div class="product"><a data-dimension112="36133684-52e2-4d27-9666-630389017d1b" data-action="Deal Block" data-label="R95H Micro RGB TV (65-inch)" data-dimension48="R95H Micro RGB TV (65-inch)" data-dimension25="$3934" href="https://www.thegoodguys.com.au/samsung-65-inches-micro-rgb-r95h-4k-smart-ai-tv-2026-mra65r95hawxxy" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="moZs7yNSiAjvShC5wmztDN" name="SAMSUNGR95H" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/moZs7yNSiAjvShC5wmztDN-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/samsung-65-inches-micro-rgb-r95h-4k-smart-ai-tv-2026-mra65r95hawxxy" target="_blank" rel="nofollow" data-dimension112="36133684-52e2-4d27-9666-630389017d1b" data-action="Deal Block" data-label="R95H Micro RGB TV (65-inch)" data-dimension48="R95H Micro RGB TV (65-inch)" data-dimension25="$3934">R95H Micro RGB TV (65-inch): was $5295 now $3934</a></strong><br>The brand‑new R95H Micro RGB TV has already landed with an EOFY discount, which means I no longer have to consider <a href="https://www.tomsguide.com/tvs/samsungs-115-inch-micro-rgb-tv-has-colours-so-good-i-briefly-considered-selling-a-kidney-to-afford-one">selling a kidney to afford one</a>. It reaches 100% of the BT.2020 colour space thanks to its Micro RGB structure and the custom processors designed specifically for this panel. Samsung’s anti‑glare treatment is included as well, giving it stronger clarity and contrast in bright viewing environments. Don't forget to click on the 'Price Check' button to get the full discount.<a class="view-deal button" href="https://www.thegoodguys.com.au/samsung-65-inches-micro-rgb-r95h-4k-smart-ai-tv-2026-mra65r95hawxxy" target="_blank" rel="nofollow" data-dimension112="36133684-52e2-4d27-9666-630389017d1b" data-action="Deal Block" data-label="R95H Micro RGB TV (65-inch)" data-dimension48="R95H Micro RGB TV (65-inch)" data-dimension25="$3934">View Deal</a></p></div><div class="product"><a data-dimension112="c9dbe422-3a2a-4e32-8955-db26d3125565" data-action="Deal Block" data-label="Bravia 8 II (65-inch)" data-dimension48="Bravia 8 II (65-inch)" data-dimension25="$3985" href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8m2-4k-oled-tv-2025-k65xr80m2" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1509px;"><p class="vanilla-image-block" style="padding-top:99.34%;"><img id="ePDgAxasepVKPVeiVkbAeL" name="Sony Bravia 8 II square.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ePDgAxasepVKPVeiVkbAeL-1920-80.jpg" mos="" align="middle" fullscreen="" width="1509" height="1499" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8m2-4k-oled-tv-2025-k65xr80m2" target="_blank" rel="nofollow" data-dimension112="c9dbe422-3a2a-4e32-8955-db26d3125565" data-action="Deal Block" data-label="Bravia 8 II (65-inch)" data-dimension48="Bravia 8 II (65-inch)" data-dimension25="$3985">Bravia 8 II (65-inch): was $5299 now $3985</a></strong><br>Sony's going all-in on True RGB tech this year, but if you'd still prefer one of the manufacturer's OLED TVs, this model from last year is your best bet. The Bravia 8 II pairs Sony’s XR Processor with its XR Triluminos Max colour system to make full use of the QD‑OLED panel’s quantum dot capabilities, producing richer, more accurate colours and noticeably improved brightness. Make sure you click on the 'Price Check' button to get the full discount.<a class="view-deal button" href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8m2-4k-oled-tv-2025-k65xr80m2" target="_blank" rel="nofollow" data-dimension112="c9dbe422-3a2a-4e32-8955-db26d3125565" data-action="Deal Block" data-label="Bravia 8 II (65-inch)" data-dimension48="Bravia 8 II (65-inch)" data-dimension25="$3985">View Deal</a></p></div><div class="product"><a data-dimension112="543d6671-629a-4df6-ab57-1e839f2046ad" data-action="Deal Block" data-label="Bravia 8 (65-inch)" data-dimension48="Bravia 8 (65-inch)" data-dimension25="$2902" href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8-4k-oled-google-tv-24-k65xr80" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1209px;"><p class="vanilla-image-block" style="padding-top:99.17%;"><img id="esXdQrGpxPaZBPrwgNPAUP" name="1730807136.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/esXdQrGpxPaZBPrwgNPAUP-1920-80.jpg" mos="" align="middle" fullscreen="" width="1209" height="1199" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8-4k-oled-google-tv-24-k65xr80" target="_blank" rel="nofollow" data-dimension112="543d6671-629a-4df6-ab57-1e839f2046ad" data-action="Deal Block" data-label="Bravia 8 (65-inch)" data-dimension48="Bravia 8 (65-inch)" data-dimension25="$2902">Bravia 8 (65-inch): was $4495 now $2902</a></strong><br>Sony's Bravia 8 is an easy recommendation for most households. Even though it uses a ‘standard’ WOLED panel rather than the QD-OLED one like the previous TV, Sony’s processing gives it a real lift — delivering impressive brightness and wonderfully natural colour that feels true to the brand’s signature look. The built‑in audio system is another highlight, standing out as one of the strongest integrated setups you’ll find in a modern TV. Just remember to click on the 'Price Check' button to get the full discount.<a class="view-deal button" href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8-4k-oled-google-tv-24-k65xr80" target="_blank" rel="nofollow" data-dimension112="543d6671-629a-4df6-ab57-1e839f2046ad" data-action="Deal Block" data-label="Bravia 8 (65-inch)" data-dimension48="Bravia 8 (65-inch)" data-dimension25="$2902">View Deal</a></p></div><div class="product"><a data-dimension112="d87c48b4-182b-4ade-95a0-3c2b704a1217" data-action="Deal Block" data-label="U8500F Crystal UHD 4K TV (75-inch)" data-dimension48="U8500F Crystal UHD 4K TV (75-inch)" data-dimension25="$1394" href="https://www.thegoodguys.com.au/samsung-75-inches-u8500f-crystal-uhd-4k-smart-tv-2025-ua75u8500fwxxy" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="n98XU33bFAb9HxXhhNEs6e" name="Samsung-U8500F-Crystal-UHD-4K-Smart-TV" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/n98XU33bFAb9HxXhhNEs6e-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/samsung-75-inches-u8500f-crystal-uhd-4k-smart-tv-2025-ua75u8500fwxxy" target="_blank" rel="nofollow" data-dimension112="d87c48b4-182b-4ade-95a0-3c2b704a1217" data-action="Deal Block" data-label="U8500F Crystal UHD 4K TV (75-inch)" data-dimension48="U8500F Crystal UHD 4K TV (75-inch)" data-dimension25="$1394">U8500F Crystal UHD 4K TV (75-inch): was $1954 now $1394</a></strong><br>Admittedly, the U8500F hasn't been at full retail price for a while, but this is a great price to pay for a larger Samsung telly. The 75-inch Samsung U8500F Crystal UHD TV offers 4K resolution and HDR10+ support, though gamers should note that it only has a 50Hz native refresh rate. Still, this is a great, budget-friendly TV, especially at this price. Don't forget to click on the 'Price Check' button to get the full discount.<a class="view-deal button" href="https://www.thegoodguys.com.au/samsung-75-inches-u8500f-crystal-uhd-4k-smart-tv-2025-ua75u8500fwxxy" target="_blank" rel="nofollow" data-dimension112="d87c48b4-182b-4ade-95a0-3c2b704a1217" data-action="Deal Block" data-label="U8500F Crystal UHD 4K TV (75-inch)" data-dimension48="U8500F Crystal UHD 4K TV (75-inch)" data-dimension25="$1394">View Deal</a></p></div><div class="product"><a data-dimension112="6893943a-a64f-405d-9ded-db048b374d35" data-action="Deal Block" data-label="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension48="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension25="$1695" href="https://www.thegoodguys.com.au/sony-75-inches-bravia-3-4k-hdr-google-tv-24-k75s30" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="ibkAkuAwgw3XEDD8Y3wKJL" name="Sony-Bravia-3-75-inch" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ibkAkuAwgw3XEDD8Y3wKJL-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/sony-75-inches-bravia-3-4k-hdr-google-tv-24-k75s30" target="_blank" rel="nofollow" data-dimension112="6893943a-a64f-405d-9ded-db048b374d35" data-action="Deal Block" data-label="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension48="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension25="$1695">Bravia 3 Direct LED 4K TV (75-inch): was $1895 now $1695</a></strong><br>Sure, Direct LED technology may not match the sophistication of Sony's Mini LED and OLED TVs, but you'd be amazed at what the company has accomplished with this older backlighting system. Thanks to Sony's advanced Triluminos Pro technology and the powerful 4K HDR Processor X1, you get smooth motion and enhanced contrast. Plus, it’s an excellent deal for a 75-inch TV. Just make sure you hit the 'price check' button for the best price.<a class="view-deal button" href="https://www.thegoodguys.com.au/sony-75-inches-bravia-3-4k-hdr-google-tv-24-k75s30" target="_blank" rel="nofollow" data-dimension112="6893943a-a64f-405d-9ded-db048b374d35" data-action="Deal Block" data-label="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension48="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension25="$1695">View Deal</a></p></div><div class="product"><a data-dimension112="93fbde86-cec1-4b1a-8c06-242d631b1a95" data-action="Deal Block" data-label="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension48="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension25="$1364" href="https://www.thegoodguys.com.au/hisense-55-inches-canvas-s7nau-4k-qled-smart-tv-2024-55s7nau" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="nfNzD5FPa7h84sDjPkn5QC" name="Hisense-Canvas-S7NAU-QLED-TV" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/nfNzD5FPa7h84sDjPkn5QC-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/hisense-55-inches-canvas-s7nau-4k-qled-smart-tv-2024-55s7nau" target="_blank" rel="nofollow" data-dimension112="93fbde86-cec1-4b1a-8c06-242d631b1a95" data-action="Deal Block" data-label="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension48="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension25="$1364">Canvas S7NAU 4K QLED TV (55-inch): was $1795 now $1364</a></strong><br>Challenging Samsung in the "TVs that look like framed art" segment, the Hisense Canvas S7NAU 4K QLED TV stands out as an affordable option, especially at its current discounted price. It delivers vivid 4K quantum dot colour, a native 144Hz refresh rate (with simulated 240 motion rate), and operates on the VIDAA U8 smart TV OS. Plus, it supports Dolby Vision, which is a feature Samsung lacks. Make sure to hit the 'Price Beat' button.<a class="view-deal button" href="https://www.thegoodguys.com.au/hisense-55-inches-canvas-s7nau-4k-qled-smart-tv-2024-55s7nau" target="_blank" rel="nofollow" data-dimension112="93fbde86-cec1-4b1a-8c06-242d631b1a95" data-action="Deal Block" data-label="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension48="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension25="$1364">View Deal</a></p></div><div class="product"><a data-dimension112="f88da6bc-b573-4658-a652-769e66fb7f94" data-action="Deal Block" data-label="Bravia 5 Mini LED TV (85-inch)" data-dimension48="Bravia 5 Mini LED TV (85-inch)" data-dimension25="$2818" href="https://www.thegoodguys.com.au/sony-85-inches-bravia-5-4k-mini-led-tv-2025-k85xr50" 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="frsc6M6z49Qgn5PXmQsAka" name="sonuybravia5" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/frsc6M6z49Qgn5PXmQsAka-1920-80.jpg" mos="" align="middle" fullscreen="" width="900" height="900" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/sony-85-inches-bravia-5-4k-mini-led-tv-2025-k85xr50" target="_blank" rel="nofollow" data-dimension112="f88da6bc-b573-4658-a652-769e66fb7f94" data-action="Deal Block" data-label="Bravia 5 Mini LED TV (85-inch)" data-dimension48="Bravia 5 Mini LED TV (85-inch)" data-dimension25="$2818">Bravia 5 Mini LED TV (85-inch): was $3795 now $2818</a></strong><br>The Bravia 5 is Sony's mid-range mini-LED TV and the replacement for the excellent X90L. We've not reviewed it fully just yet, but we've gone <a href="https://www.tomsguide.com/tvs/i-went-hands-on-with-the-98-inch-sony-bravia-5-and-this-mini-led-tv-blew-me-away">eyes-on with the 98-inch version</a> and found it to deliver a sumptuous picture, with deep blacks and bright, punchy colours. It does suffer from some off-axis viewing issues, but otherwise this is a superb screen. For a TV this size and spec, we reckon this discount makes this Bravia a good option for those looking to upgrade to a bigger display, even though this isn't the cheapest price we've seen on this telly. Make sure to click the 'Price Check' button to get the full discount. <a class="view-deal button" href="https://www.thegoodguys.com.au/sony-85-inches-bravia-5-4k-mini-led-tv-2025-k85xr50" target="_blank" rel="nofollow" data-dimension112="f88da6bc-b573-4658-a652-769e66fb7f94" data-action="Deal Block" data-label="Bravia 5 Mini LED TV (85-inch)" data-dimension48="Bravia 5 Mini LED TV (85-inch)" data-dimension25="$2818">View Deal</a></p></div><p>So there you have it — my roundup of best TV deals from this year’s EOFY sales. If you’ve been thinking about an OLED upgrade, want to see how the latest RGB tech performs, or simply feel like it’s time for a larger TV, these options are well worth a look. But you'd better act fast — the discounts won’t stick around past July 1.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/4k-tvs/dont-miss-out-these-last-minute-eofy-tv-deals-are-your-final-chance-to-score-a-new-set-at-clearance-prices</link>
                                                                            <description>
                            <![CDATA[ Retailers are looking to offload stock, which makes EOFY is the best time to buy a new television. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">UTQ47QYvTp6DhHyutVrPd9</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/hRKfTvpmf74fGmGhuzvCf6-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 29 Jun 2026 06:52:39 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[4k TVs]]></category>
                                                    <category><![CDATA[TVs]]></category>
                                                                                                                    <dc:creator><![CDATA[ Stephen Lambrechts ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/JN3jEQpqyyt32as8AJJqXe-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Stephen Lambrechts is the Managing Editor of Tom&#039;s Guide AU and has written professionally across the categories of tech, film, television and gaming for the last 15 years. Before Tom&#039;s Guide, he spent several years as a Senior Journalist at TechRadar, had a brief stint as Editor in Chief at Official Xbox Magazine Australia, and worked at IGN. He&#039;s also written for such mags and sites as APC, TechLife Australia, T3, FilmInk, Daily Telegraph and AskMen. Additionally, he&#039;s a Tomatometer-approved critic on Rotten Tomatoes, and has offered his tech advice on radio and television.&lt;/p&gt;&lt;p&gt;He&#039;s an expert when it comes to smartphones, TVs, gaming and streaming, having contributed many reviews in each of those categories over the years. Despite his many articles on streaming, he&#039;s a passionate collector of physical media who enjoys listening to vinyl records and acquiring obscure horror movies from the &#039;60s, &#039;70s and 80s. He can usually be seen rocking the latest retro sneaker releases, particularly Air Jordans and Reebok Pumps. Occasionally, he also indulges in other non-hipster stuff, like hiking.&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/hRKfTvpmf74fGmGhuzvCf6-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide / Edited with Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Various TVs on podiums with Killer Deals badge overlaid]]></media:description>                                                            <media:text><![CDATA[Various TVs on podiums with Killer Deals badge overlaid]]></media:text>
                                <media:title type="plain"><![CDATA[Various TVs on podiums with Killer Deals badge overlaid]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/hRKfTvpmf74fGmGhuzvCf6-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The end is near for this year’s <a href="https://www.tomsguide.com/deals/eofy-sale-australia">EOFY sales</a>, with the price‑cut frenzy wrapping up as June 30 comes to a close. Even so, there’s still time to snag a last‑minute TV bargain before the new financial year kicks in.</p><p>If you’ve been thinking about upgrading your TV, this is the moment to stop scrolling and start grabbing. Retailers are clearing out stock fast, and the mix of price drops, bonus offers and surprise markdowns means you can pick up a genuinely great screen for far less than you’d expect. </p><p>I’ve seen bargains you wouldn’t believe. Door‑buster TV deals stacked higher than warehouse shelves, flash sales that lit up retailer sites like neon signs in the night. I watched prices plunge in the final hours of EOFY, deeper than anyone thought possible. All of those deals will be lost in time, like tears in rain. Time to buy.</p><p>I cannot stress this enough — this is your last chance to score an epic TV deal at clearance prices before the EOFY sales are over. If you’ve been waiting for the right moment to refresh your home setup with a new Samsung, Sony or Hisense television, you won't find a better time than now — here are my picks for the best last-minute EOFY TV deals.</p><div class="product"><a data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999" href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="xA3gfVDXp63Y6AQJqMGjHi" name="Sony-Bravia-7-II" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/xA3gfVDXp63Y6AQJqMGjHi-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow" data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999">Bravia 7 II True RGB TV (85-inch): was $7499 now $5999</a></strong><br>Sony's new True RGB TV range, which includes the Bravia 7 II, is pushing display technology to new heights thanks to an incredible backlight system that uses individually controlled red, green and blue LEDs to deliver pure colour and brightness to its screen. That means OLED-level contrast and immense colour accuracy at any brightness level. Right now, the Bravia 7 II is currently discounted by up to AU$1,500 on Sony's online store for EOFY. </p><p>50-inch: <del>AU$2,699</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-50xr70m2-az1" target="_blank">AU$2,299</a><br>55-inch: <del>AU$2,999</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-55xr70m2-az1" target="_blank">AU$2,499</a><br>65-inch: <del>AU$3,999</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-65xr70m2-az1" target="_blank">AU$3,299</a><br>75-inch: <del>AU$5,499</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-75xr70m2-az1" target="_blank">AU$4,499</a><br>85-inch: <del>AU$7,499</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank">AU$5,999</a><a class="view-deal button" href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow" data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999">View Deal</a></p></div><div class="product"><a data-dimension112="158599ca-0104-4519-82b2-05d61dc055f5" data-action="Deal Block" data-label="S90H (65-inch)" data-dimension48="S90H (65-inch)" data-dimension25="$2778" href="https://www.thegoodguys.com.au/samsung-65-inches-oled-s90h-4k-smart-ai-tv-2026-qa65s90hawxxy" 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="4WEZaDtsLkVzw5ZzmTd3zX" name="samsung-s90h" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4WEZaDtsLkVzw5ZzmTd3zX-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/samsung-65-inches-oled-s90h-4k-smart-ai-tv-2026-qa65s90hawxxy" target="_blank" rel="nofollow" data-dimension112="158599ca-0104-4519-82b2-05d61dc055f5" data-action="Deal Block" data-label="S90H (65-inch)" data-dimension48="S90H (65-inch)" data-dimension25="$2778">S90H (65-inch): was $3595 now $2778</a></strong><br>I was <a href="https://www.tomsguide.com/tvs/samsungs-2026-australian-tv-lineup-is-packed-with-upgrades-but-i-was-utterly-blown-away-by-one-model-in-particular">blown away</a> by Samsung’s S90H OLED TV when I saw it earlier this year, and this particular model is the one I would pick from Samsung's 2026 lineup. It's basically the same panel as the flagship S95H, minus the polarising metallic frame design, meaning it also boasts a glare-free display. Make sure you click on the 'Price Check' button to get the full discount.<a class="view-deal button" href="https://www.thegoodguys.com.au/samsung-65-inches-oled-s90h-4k-smart-ai-tv-2026-qa65s90hawxxy" target="_blank" rel="nofollow" data-dimension112="158599ca-0104-4519-82b2-05d61dc055f5" data-action="Deal Block" data-label="S90H (65-inch)" data-dimension48="S90H (65-inch)" data-dimension25="$2778">View Deal</a></p></div><div class="product"><a data-dimension112="36133684-52e2-4d27-9666-630389017d1b" data-action="Deal Block" data-label="R95H Micro RGB TV (65-inch)" data-dimension48="R95H Micro RGB TV (65-inch)" data-dimension25="$3934" href="https://www.thegoodguys.com.au/samsung-65-inches-micro-rgb-r95h-4k-smart-ai-tv-2026-mra65r95hawxxy" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="moZs7yNSiAjvShC5wmztDN" name="SAMSUNGR95H" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/moZs7yNSiAjvShC5wmztDN-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/samsung-65-inches-micro-rgb-r95h-4k-smart-ai-tv-2026-mra65r95hawxxy" target="_blank" rel="nofollow" data-dimension112="36133684-52e2-4d27-9666-630389017d1b" data-action="Deal Block" data-label="R95H Micro RGB TV (65-inch)" data-dimension48="R95H Micro RGB TV (65-inch)" data-dimension25="$3934">R95H Micro RGB TV (65-inch): was $5295 now $3934</a></strong><br>The brand‑new R95H Micro RGB TV has already landed with an EOFY discount, which means I no longer have to consider <a href="https://www.tomsguide.com/tvs/samsungs-115-inch-micro-rgb-tv-has-colours-so-good-i-briefly-considered-selling-a-kidney-to-afford-one">selling a kidney to afford one</a>. It reaches 100% of the BT.2020 colour space thanks to its Micro RGB structure and the custom processors designed specifically for this panel. Samsung’s anti‑glare treatment is included as well, giving it stronger clarity and contrast in bright viewing environments. Don't forget to click on the 'Price Check' button to get the full discount.<a class="view-deal button" href="https://www.thegoodguys.com.au/samsung-65-inches-micro-rgb-r95h-4k-smart-ai-tv-2026-mra65r95hawxxy" target="_blank" rel="nofollow" data-dimension112="36133684-52e2-4d27-9666-630389017d1b" data-action="Deal Block" data-label="R95H Micro RGB TV (65-inch)" data-dimension48="R95H Micro RGB TV (65-inch)" data-dimension25="$3934">View Deal</a></p></div><div class="product"><a data-dimension112="c9dbe422-3a2a-4e32-8955-db26d3125565" data-action="Deal Block" data-label="Bravia 8 II (65-inch)" data-dimension48="Bravia 8 II (65-inch)" data-dimension25="$3985" href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8m2-4k-oled-tv-2025-k65xr80m2" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1509px;"><p class="vanilla-image-block" style="padding-top:99.34%;"><img id="ePDgAxasepVKPVeiVkbAeL" name="Sony Bravia 8 II square.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ePDgAxasepVKPVeiVkbAeL-1920-80.jpg" mos="" align="middle" fullscreen="" width="1509" height="1499" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8m2-4k-oled-tv-2025-k65xr80m2" target="_blank" rel="nofollow" data-dimension112="c9dbe422-3a2a-4e32-8955-db26d3125565" data-action="Deal Block" data-label="Bravia 8 II (65-inch)" data-dimension48="Bravia 8 II (65-inch)" data-dimension25="$3985">Bravia 8 II (65-inch): was $5299 now $3985</a></strong><br>Sony's going all-in on True RGB tech this year, but if you'd still prefer one of the manufacturer's OLED TVs, this model from last year is your best bet. The Bravia 8 II pairs Sony’s XR Processor with its XR Triluminos Max colour system to make full use of the QD‑OLED panel’s quantum dot capabilities, producing richer, more accurate colours and noticeably improved brightness. Make sure you click on the 'Price Check' button to get the full discount.<a class="view-deal button" href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8m2-4k-oled-tv-2025-k65xr80m2" target="_blank" rel="nofollow" data-dimension112="c9dbe422-3a2a-4e32-8955-db26d3125565" data-action="Deal Block" data-label="Bravia 8 II (65-inch)" data-dimension48="Bravia 8 II (65-inch)" data-dimension25="$3985">View Deal</a></p></div><div class="product"><a data-dimension112="543d6671-629a-4df6-ab57-1e839f2046ad" data-action="Deal Block" data-label="Bravia 8 (65-inch)" data-dimension48="Bravia 8 (65-inch)" data-dimension25="$2902" href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8-4k-oled-google-tv-24-k65xr80" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1209px;"><p class="vanilla-image-block" style="padding-top:99.17%;"><img id="esXdQrGpxPaZBPrwgNPAUP" name="1730807136.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/esXdQrGpxPaZBPrwgNPAUP-1920-80.jpg" mos="" align="middle" fullscreen="" width="1209" height="1199" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8-4k-oled-google-tv-24-k65xr80" target="_blank" rel="nofollow" data-dimension112="543d6671-629a-4df6-ab57-1e839f2046ad" data-action="Deal Block" data-label="Bravia 8 (65-inch)" data-dimension48="Bravia 8 (65-inch)" data-dimension25="$2902">Bravia 8 (65-inch): was $4495 now $2902</a></strong><br>Sony's Bravia 8 is an easy recommendation for most households. Even though it uses a ‘standard’ WOLED panel rather than the QD-OLED one like the previous TV, Sony’s processing gives it a real lift — delivering impressive brightness and wonderfully natural colour that feels true to the brand’s signature look. The built‑in audio system is another highlight, standing out as one of the strongest integrated setups you’ll find in a modern TV. Just remember to click on the 'Price Check' button to get the full discount.<a class="view-deal button" href="https://www.thegoodguys.com.au/sony-65-inches-bravia-8-4k-oled-google-tv-24-k65xr80" target="_blank" rel="nofollow" data-dimension112="543d6671-629a-4df6-ab57-1e839f2046ad" data-action="Deal Block" data-label="Bravia 8 (65-inch)" data-dimension48="Bravia 8 (65-inch)" data-dimension25="$2902">View Deal</a></p></div><div class="product"><a data-dimension112="d87c48b4-182b-4ade-95a0-3c2b704a1217" data-action="Deal Block" data-label="U8500F Crystal UHD 4K TV (75-inch)" data-dimension48="U8500F Crystal UHD 4K TV (75-inch)" data-dimension25="$1394" href="https://www.thegoodguys.com.au/samsung-75-inches-u8500f-crystal-uhd-4k-smart-tv-2025-ua75u8500fwxxy" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="n98XU33bFAb9HxXhhNEs6e" name="Samsung-U8500F-Crystal-UHD-4K-Smart-TV" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/n98XU33bFAb9HxXhhNEs6e-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/samsung-75-inches-u8500f-crystal-uhd-4k-smart-tv-2025-ua75u8500fwxxy" target="_blank" rel="nofollow" data-dimension112="d87c48b4-182b-4ade-95a0-3c2b704a1217" data-action="Deal Block" data-label="U8500F Crystal UHD 4K TV (75-inch)" data-dimension48="U8500F Crystal UHD 4K TV (75-inch)" data-dimension25="$1394">U8500F Crystal UHD 4K TV (75-inch): was $1954 now $1394</a></strong><br>Admittedly, the U8500F hasn't been at full retail price for a while, but this is a great price to pay for a larger Samsung telly. The 75-inch Samsung U8500F Crystal UHD TV offers 4K resolution and HDR10+ support, though gamers should note that it only has a 50Hz native refresh rate. Still, this is a great, budget-friendly TV, especially at this price. Don't forget to click on the 'Price Check' button to get the full discount.<a class="view-deal button" href="https://www.thegoodguys.com.au/samsung-75-inches-u8500f-crystal-uhd-4k-smart-tv-2025-ua75u8500fwxxy" target="_blank" rel="nofollow" data-dimension112="d87c48b4-182b-4ade-95a0-3c2b704a1217" data-action="Deal Block" data-label="U8500F Crystal UHD 4K TV (75-inch)" data-dimension48="U8500F Crystal UHD 4K TV (75-inch)" data-dimension25="$1394">View Deal</a></p></div><div class="product"><a data-dimension112="6893943a-a64f-405d-9ded-db048b374d35" data-action="Deal Block" data-label="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension48="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension25="$1695" href="https://www.thegoodguys.com.au/sony-75-inches-bravia-3-4k-hdr-google-tv-24-k75s30" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="ibkAkuAwgw3XEDD8Y3wKJL" name="Sony-Bravia-3-75-inch" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ibkAkuAwgw3XEDD8Y3wKJL-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/sony-75-inches-bravia-3-4k-hdr-google-tv-24-k75s30" target="_blank" rel="nofollow" data-dimension112="6893943a-a64f-405d-9ded-db048b374d35" data-action="Deal Block" data-label="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension48="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension25="$1695">Bravia 3 Direct LED 4K TV (75-inch): was $1895 now $1695</a></strong><br>Sure, Direct LED technology may not match the sophistication of Sony's Mini LED and OLED TVs, but you'd be amazed at what the company has accomplished with this older backlighting system. Thanks to Sony's advanced Triluminos Pro technology and the powerful 4K HDR Processor X1, you get smooth motion and enhanced contrast. Plus, it’s an excellent deal for a 75-inch TV. Just make sure you hit the 'price check' button for the best price.<a class="view-deal button" href="https://www.thegoodguys.com.au/sony-75-inches-bravia-3-4k-hdr-google-tv-24-k75s30" target="_blank" rel="nofollow" data-dimension112="6893943a-a64f-405d-9ded-db048b374d35" data-action="Deal Block" data-label="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension48="Bravia 3 Direct LED 4K TV (75-inch)" data-dimension25="$1695">View Deal</a></p></div><div class="product"><a data-dimension112="93fbde86-cec1-4b1a-8c06-242d631b1a95" data-action="Deal Block" data-label="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension48="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension25="$1364" href="https://www.thegoodguys.com.au/hisense-55-inches-canvas-s7nau-4k-qled-smart-tv-2024-55s7nau" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="nfNzD5FPa7h84sDjPkn5QC" name="Hisense-Canvas-S7NAU-QLED-TV" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/nfNzD5FPa7h84sDjPkn5QC-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/hisense-55-inches-canvas-s7nau-4k-qled-smart-tv-2024-55s7nau" target="_blank" rel="nofollow" data-dimension112="93fbde86-cec1-4b1a-8c06-242d631b1a95" data-action="Deal Block" data-label="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension48="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension25="$1364">Canvas S7NAU 4K QLED TV (55-inch): was $1795 now $1364</a></strong><br>Challenging Samsung in the "TVs that look like framed art" segment, the Hisense Canvas S7NAU 4K QLED TV stands out as an affordable option, especially at its current discounted price. It delivers vivid 4K quantum dot colour, a native 144Hz refresh rate (with simulated 240 motion rate), and operates on the VIDAA U8 smart TV OS. Plus, it supports Dolby Vision, which is a feature Samsung lacks. Make sure to hit the 'Price Beat' button.<a class="view-deal button" href="https://www.thegoodguys.com.au/hisense-55-inches-canvas-s7nau-4k-qled-smart-tv-2024-55s7nau" target="_blank" rel="nofollow" data-dimension112="93fbde86-cec1-4b1a-8c06-242d631b1a95" data-action="Deal Block" data-label="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension48="Canvas S7NAU 4K QLED TV (55-inch)" data-dimension25="$1364">View Deal</a></p></div><div class="product"><a data-dimension112="f88da6bc-b573-4658-a652-769e66fb7f94" data-action="Deal Block" data-label="Bravia 5 Mini LED TV (85-inch)" data-dimension48="Bravia 5 Mini LED TV (85-inch)" data-dimension25="$2818" href="https://www.thegoodguys.com.au/sony-85-inches-bravia-5-4k-mini-led-tv-2025-k85xr50" 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="frsc6M6z49Qgn5PXmQsAka" name="sonuybravia5" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/frsc6M6z49Qgn5PXmQsAka-1920-80.jpg" mos="" align="middle" fullscreen="" width="900" height="900" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.thegoodguys.com.au/sony-85-inches-bravia-5-4k-mini-led-tv-2025-k85xr50" target="_blank" rel="nofollow" data-dimension112="f88da6bc-b573-4658-a652-769e66fb7f94" data-action="Deal Block" data-label="Bravia 5 Mini LED TV (85-inch)" data-dimension48="Bravia 5 Mini LED TV (85-inch)" data-dimension25="$2818">Bravia 5 Mini LED TV (85-inch): was $3795 now $2818</a></strong><br>The Bravia 5 is Sony's mid-range mini-LED TV and the replacement for the excellent X90L. We've not reviewed it fully just yet, but we've gone <a href="https://www.tomsguide.com/tvs/i-went-hands-on-with-the-98-inch-sony-bravia-5-and-this-mini-led-tv-blew-me-away">eyes-on with the 98-inch version</a> and found it to deliver a sumptuous picture, with deep blacks and bright, punchy colours. It does suffer from some off-axis viewing issues, but otherwise this is a superb screen. For a TV this size and spec, we reckon this discount makes this Bravia a good option for those looking to upgrade to a bigger display, even though this isn't the cheapest price we've seen on this telly. Make sure to click the 'Price Check' button to get the full discount. <a class="view-deal button" href="https://www.thegoodguys.com.au/sony-85-inches-bravia-5-4k-mini-led-tv-2025-k85xr50" target="_blank" rel="nofollow" data-dimension112="f88da6bc-b573-4658-a652-769e66fb7f94" data-action="Deal Block" data-label="Bravia 5 Mini LED TV (85-inch)" data-dimension48="Bravia 5 Mini LED TV (85-inch)" data-dimension25="$2818">View Deal</a></p></div><p>So there you have it — my roundup of best TV deals from this year’s EOFY sales. If you’ve been thinking about an OLED upgrade, want to see how the latest RGB tech performs, or simply feel like it’s time for a larger TV, these options are well worth a look. But you'd better act fast — the discounts won’t stick around past July 1.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Sony's new Bravia 7 II True RGB TV has impressed the hell out of me, and now it's up to AU$1,500 off for EOFY ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I’ve spent the past couple of weeks putting Sony’s new Bravia 7 II True RGB TV through its paces (with a deeper dive coming next week), and so far I've been seriously impressed by its contrast, colour accuracy and brightness.</p><p>Despite being one step below Sony's flagship Bravia 9 II, the Bravia 7 II holds its own with the best of them. I went in expecting exceptionally vivid colours due to its use of individually controlled red, green and blue backlights, but what really blew me away was how it was able to achieve OLED-like black levels without any noticeable light bleed — all while keeping the kind of brightness you’d normally only see from the best Mini LED TVs.</p><p>As pointed out in our <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Bravia 7 II review</a>, the TV is a beast when it comes to HDR performance, and my favourite thing about it has to be that it comes factory-calibrated to industry-standard levels, meaning it provides a look based on Sony's own master monitors (used by Hollywood colourists) right out of the box.</p><p>Best of all? The brand-new Bravia 7 II has already been discounted by upwards of AU$1,500 (depending on the retailer) as part of this year's <a href="https://www.tomsguide.com/deals/eofy-sale-australia">EOFY sales</a>. The biggest discount can be found on 85-inch model, which will usually set you back AU$7,499 but is currently down to <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank">AU$5,999 on Sony's online store</a> with free shipping included. Alternatively, you could pick it up <a href="https://www.thegoodguys.com.au/sony-85-inches-bravia-7-ii-true-rgb-4k-ai-google-tv-with-gemini-2026-k85xr70m2" target="_blank">from The Good Guys for just AU$5,985</a>, though that doesn't include shipping costs.</p><div class="product"><a data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999" href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="xA3gfVDXp63Y6AQJqMGjHi" name="Sony-Bravia-7-II" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/xA3gfVDXp63Y6AQJqMGjHi-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow" data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999">Bravia 7 II True RGB TV (85-inch): was $7499 now $5999</a></strong><br>Sony's new True RGB TV range, which includes the Bravia 7 II, is pushing display technology to new heights thanks to an incredible backlight system that uses individually controlled red, green and blue LEDs to deliver pure colour and brightness to its screen. That means OLED-level contrast and immense colour accuracy at any brightness level. Right now, the Bravia 7 II is currently discounted by up to AU$1,500 on Sony's online store for EOFY. </p><p>50-inch: <del>AU$2,699</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-50xr70m2-az1" target="_blank">AU$2,299</a><br>55-inch: <del>AU$2,999</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-55xr70m2-az1" target="_blank">AU$2,499</a><br>65-inch: <del>AU$3,999</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-65xr70m2-az1" target="_blank">AU$3,299</a><br>75-inch: <del>AU$5,499</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-75xr70m2-az1" target="_blank">AU$4,499</a><br>85-inch: <del>AU$7,499</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank">AU$5,999</a><a class="view-deal button" href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow" data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999">View Deal</a></p></div><p>While the best deal can be had on the 85-incher, the next biggest discount can be found on the 75-inch model, which has dropped by AU$1,000 to just <del>AU$5,499</del> AU$4,499. </p><p>Meanwhile, the 65‑inch model is now AU$700 off, dropping from AU$3,999 to AU$3,299, while the 55‑inch falls from AU$2,999 to AU$2,499, and the 50‑inch from AU$2,699 to AU$2,299.</p><p>Regardless of your preferred size option, those are are excellent deals. That said, you'd better hurry if you want to take advantage of this EOFY offer, as it'll expire after 11:59pm AEST on June 30, 2026.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/computing/tablets/sonys-new-bravia-7-ii-true-rgb-tv-has-impressed-the-hell-out-of-me-and-now-its-au700-off-for-eofy</link>
                                                                            <description>
                            <![CDATA[ Sony’s Bravia 7 II has barely hit the shelves, but its early EOFY price drop makes it one of the best tax‑time deals going. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">FbJPrmUbu2kt9LTHAhL9v9</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/xXtQCQnkMw4Z72NFgKN76N-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 26 Jun 2026 03:22:09 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[4k TVs]]></category>
                                                    <category><![CDATA[TVs]]></category>
                                                                                                                    <dc:creator><![CDATA[ Stephen Lambrechts ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/JN3jEQpqyyt32as8AJJqXe-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Stephen Lambrechts is the Managing Editor of Tom&#039;s Guide AU and has written professionally across the categories of tech, film, television and gaming for the last 15 years. Before Tom&#039;s Guide, he spent several years as a Senior Journalist at TechRadar, had a brief stint as Editor in Chief at Official Xbox Magazine Australia, and worked at IGN. He&#039;s also written for such mags and sites as APC, TechLife Australia, T3, FilmInk, Daily Telegraph and AskMen. Additionally, he&#039;s a Tomatometer-approved critic on Rotten Tomatoes, and has offered his tech advice on radio and television.&lt;/p&gt;&lt;p&gt;He&#039;s an expert when it comes to smartphones, TVs, gaming and streaming, having contributed many reviews in each of those categories over the years. Despite his many articles on streaming, he&#039;s a passionate collector of physical media who enjoys listening to vinyl records and acquiring obscure horror movies from the &#039;60s, &#039;70s and 80s. He can usually be seen rocking the latest retro sneaker releases, particularly Air Jordans and Reebok Pumps. Occasionally, he also indulges in other non-hipster stuff, like hiking.&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/xXtQCQnkMw4Z72NFgKN76N-1920-80.jpg">
                                                            <media:credit><![CDATA[Sony]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Lifestyle image of the Sony Bravia 7 II True RGB TV displayed in a living room environment with &#039;Killer Deals&#039; badge overlaid]]></media:description>                                                            <media:text><![CDATA[Lifestyle image of the Sony Bravia 7 II True RGB TV displayed in a living room environment with &#039;Killer Deals&#039; badge overlaid]]></media:text>
                                <media:title type="plain"><![CDATA[Lifestyle image of the Sony Bravia 7 II True RGB TV displayed in a living room environment with &#039;Killer Deals&#039; badge overlaid]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/xXtQCQnkMw4Z72NFgKN76N-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I’ve spent the past couple of weeks putting Sony’s new Bravia 7 II True RGB TV through its paces (with a deeper dive coming next week), and so far I've been seriously impressed by its contrast, colour accuracy and brightness.</p><p>Despite being one step below Sony's flagship Bravia 9 II, the Bravia 7 II holds its own with the best of them. I went in expecting exceptionally vivid colours due to its use of individually controlled red, green and blue backlights, but what really blew me away was how it was able to achieve OLED-like black levels without any noticeable light bleed — all while keeping the kind of brightness you’d normally only see from the best Mini LED TVs.</p><p>As pointed out in our <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Bravia 7 II review</a>, the TV is a beast when it comes to HDR performance, and my favourite thing about it has to be that it comes factory-calibrated to industry-standard levels, meaning it provides a look based on Sony's own master monitors (used by Hollywood colourists) right out of the box.</p><p>Best of all? The brand-new Bravia 7 II has already been discounted by upwards of AU$1,500 (depending on the retailer) as part of this year's <a href="https://www.tomsguide.com/deals/eofy-sale-australia">EOFY sales</a>. The biggest discount can be found on 85-inch model, which will usually set you back AU$7,499 but is currently down to <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank">AU$5,999 on Sony's online store</a> with free shipping included. Alternatively, you could pick it up <a href="https://www.thegoodguys.com.au/sony-85-inches-bravia-7-ii-true-rgb-4k-ai-google-tv-with-gemini-2026-k85xr70m2" target="_blank">from The Good Guys for just AU$5,985</a>, though that doesn't include shipping costs.</p><div class="product"><a data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999" href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="xA3gfVDXp63Y6AQJqMGjHi" name="Sony-Bravia-7-II" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/xA3gfVDXp63Y6AQJqMGjHi-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow" data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999">Bravia 7 II True RGB TV (85-inch): was $7499 now $5999</a></strong><br>Sony's new True RGB TV range, which includes the Bravia 7 II, is pushing display technology to new heights thanks to an incredible backlight system that uses individually controlled red, green and blue LEDs to deliver pure colour and brightness to its screen. That means OLED-level contrast and immense colour accuracy at any brightness level. Right now, the Bravia 7 II is currently discounted by up to AU$1,500 on Sony's online store for EOFY. </p><p>50-inch: <del>AU$2,699</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-50xr70m2-az1" target="_blank">AU$2,299</a><br>55-inch: <del>AU$2,999</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-55xr70m2-az1" target="_blank">AU$2,499</a><br>65-inch: <del>AU$3,999</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-65xr70m2-az1" target="_blank">AU$3,299</a><br>75-inch: <del>AU$5,499</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-75xr70m2-az1" target="_blank">AU$4,499</a><br>85-inch: <del>AU$7,499</del> <a href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank">AU$5,999</a><a class="view-deal button" href="https://www.sony.com.au/bravia/products/bravia-7m2?sku=k-85xr70m2-az1" target="_blank" rel="nofollow" data-dimension112="4c383b63-fc89-4b8a-9534-7d7b465927bc" data-action="Deal Block" data-label="Bravia 7 II True RGB TV (85-inch)" data-dimension48="Bravia 7 II True RGB TV (85-inch)" data-dimension25="$5999">View Deal</a></p></div><p>While the best deal can be had on the 85-incher, the next biggest discount can be found on the 75-inch model, which has dropped by AU$1,000 to just <del>AU$5,499</del> AU$4,499. </p><p>Meanwhile, the 65‑inch model is now AU$700 off, dropping from AU$3,999 to AU$3,299, while the 55‑inch falls from AU$2,999 to AU$2,499, and the 50‑inch from AU$2,699 to AU$2,299.</p><p>Regardless of your preferred size option, those are are excellent deals. That said, you'd better hurry if you want to take advantage of this EOFY offer, as it'll expire after 11:59pm AEST on June 30, 2026.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The Sony WH-1000XM5 headphones I can't live without just hit their lowest ever price with 50% off for Prime Day ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I'm very picky when it comes to what headphones I use. I personally love Sony's EQ settings on their headphones, so I always gravitate towards their newest audio gear because I know I'm going to love it. </p><p>And while Sony has recently brought out the 1000X The Collexion headphones and has the flagship WH-1000XM6 on sale too, you'll have a hard job trying to get me to move on from my WH-1000XM5 headphones. Why? Because to me, they are perfect. </p><p>I love everything about them, from the sound to the ANC to the fit. And what's even better is that they're currently 50% off! For Prime Day, you can grab the <a href="https://www.amazon.com/Sony-WH-1000XM5-Canceling-Headphones-Optimizer/dp/B0DDV3FRHR" target="_blank" rel="nofollow">Sony WH-1000XM5 headphones for $198 at Amazon</a>. </p><ul><li><a href="https://www.amazon.com/primeday" target="_blank" rel="nofollow"><strong>shop all Prime Day deals at Amazon</strong></a></li></ul><div class="product"><a data-dimension112="acf8ef08-7035-4828-aa11-53165c530278" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$198" href="https://www.amazon.com/Sony-WH-1000XM5-Canceling-Headphones-Optimizer/dp/B0DDV3FRHR" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:360px;"><p class="vanilla-image-block" style="padding-top:138.89%;"><img id="JSuhY3GqMURjSBUEjmj2S4" name="sony-wh1000xm5-premium-noise-canceling-w-7e88ca50-f403-4f9f-8b52-d44771d32460.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/JSuhY3GqMURjSBUEjmj2S4-1920-80.jpg" mos="" align="middle" fullscreen="" width="360" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony WH-1000XM5 headphones are an excellent set of cans, and at $198, you're basically robbing Sony blind! They offer great sound with reliable active noise cancellation, making them an ideal choice for anyone who needs a reliable audio companion. The 30 hours of battery life also make them a worthy choice.<a class="view-deal button" href="https://www.amazon.com/Sony-WH-1000XM5-Canceling-Headphones-Optimizer/dp/B0DDV3FRHR" target="_blank" rel="nofollow" data-dimension112="acf8ef08-7035-4828-aa11-53165c530278" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$198">View Deal</a></p></div><p>The Sony WH-1000XM5 headphones are available in four different colors, including Smoky Pink (which I use and love), Black, Midnight Blue and Silver, so there's an option for everyone. </p><p>But when there’s a new version available, why would you buy the old model? Trust me, I hear ya — but the two sets of cans actually have a lot in common. I mean, they’re both some of the <a href="https://www.tomsguide.com/us/best-noise-cancelling-headphones,review-5566.html">best noise-canceling headphones</a> you can buy. One's just a little bit newer.</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:3755px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="unoTGyYcy8HsYM7ErciPsA" name="IMG_2839 (2).JPG" alt="Sony XM5 in there fold-flat case" src="https://cdn.mos.cms.futurecdn.net/unoTGyYcy8HsYM7ErciPsA-1920-80.jpg" mos="" align="middle" fullscreen="" width="3755" height="2112" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You get the same 30-hour battery life, and the sound profile isn't drastically different either. </p><p>If you want the comfort and stunning sound profile that comes with owning a pair of Sony’s flagship headphones — but are also conscious about your budget — then picking up the previous model is the best option.</p><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-audio-deals"><span>We're tracking all the best Prime Day audio deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Audio&min_discount_ratio=0.95&sort=date_desc&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function 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 { 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 (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';          }        }        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 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');                    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, 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.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> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/the-sony-wh-1000xm5-headphones-i-cant-live-without-just-hit-their-lowest-ever-price-with-50-percent-off-for-prime-day</link>
                                                                            <description>
                            <![CDATA[ I use the Sony WH-1000XM5 headphones every single day, and I was shocked when I saw them for 50% off and less than $200 for Amazon Prime Day. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">oXMgxsUsrgKwxa8MWPdgJo</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/RDhSNYJ2YcFbGDh3ru9BYT-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 25 Jun 2026 17:40:52 +0000</pubDate>                                                                                                                                <updated>Thu, 25 Jun 2026 17:41:22 +0000</updated>
                                                                                                                                            <category><![CDATA[Audio]]></category>
                                                                                                <author><![CDATA[ ashley.thieme@futurenet.com (Ashley Thieme) ]]></author>                    <dc:creator><![CDATA[ Ashley Thieme ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3AWovHjApwuNrSGRS6WBcL-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ashley Thieme is a staff writer on the Reviews team at Tom’s Guide where she tests out the latest tech so you can know what’s going to be worth your time and money. She has a master’s degree in Magazine Journalism and a bachelor’s degree in Journalism, Media and Sociology from Cardiff University. She has bylines in titles including Women’s Health UK, sharing the latest health and wellness news stories, and Virgin Radio UK, providing the latest entertainment news and working on celebrity interviews. She has experience reporting on a variety of topics including music, literature, motorsport, entertainment and health. In previously published work, she has reviewed live music events, books, and wellness products. She values the importance of tech enhancing your life rather than taking over, and as a music fanatic, she is always looking for the best way to listen to new music releases. Discovering the top audio equipment that enhances sound quality and provides optimum comfort is what Ashley does best. In her spare time, Ashley enjoys hitting her reading goals on Goodreads by getting into the latest novels with a cup of tea as well as getting out in the Welsh mountains for a good hike on the weekend.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/RDhSNYJ2YcFbGDh3ru9BYT-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sony WH-1000XM5 headphones]]></media:description>                                                            <media:text><![CDATA[Sony WH-1000XM5 headphones]]></media:text>
                                <media:title type="plain"><![CDATA[Sony WH-1000XM5 headphones]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/RDhSNYJ2YcFbGDh3ru9BYT-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I'm very picky when it comes to what headphones I use. I personally love Sony's EQ settings on their headphones, so I always gravitate towards their newest audio gear because I know I'm going to love it. </p><p>And while Sony has recently brought out the 1000X The Collexion headphones and has the flagship WH-1000XM6 on sale too, you'll have a hard job trying to get me to move on from my WH-1000XM5 headphones. Why? Because to me, they are perfect. </p><p>I love everything about them, from the sound to the ANC to the fit. And what's even better is that they're currently 50% off! For Prime Day, you can grab the <a href="https://www.amazon.com/Sony-WH-1000XM5-Canceling-Headphones-Optimizer/dp/B0DDV3FRHR" target="_blank" rel="nofollow">Sony WH-1000XM5 headphones for $198 at Amazon</a>. </p><ul><li><a href="https://www.amazon.com/primeday" target="_blank" rel="nofollow"><strong>shop all Prime Day deals at Amazon</strong></a></li></ul><div class="product"><a data-dimension112="acf8ef08-7035-4828-aa11-53165c530278" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$198" href="https://www.amazon.com/Sony-WH-1000XM5-Canceling-Headphones-Optimizer/dp/B0DDV3FRHR" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:360px;"><p class="vanilla-image-block" style="padding-top:138.89%;"><img id="JSuhY3GqMURjSBUEjmj2S4" name="sony-wh1000xm5-premium-noise-canceling-w-7e88ca50-f403-4f9f-8b52-d44771d32460.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/JSuhY3GqMURjSBUEjmj2S4-1920-80.jpg" mos="" align="middle" fullscreen="" width="360" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony WH-1000XM5 headphones are an excellent set of cans, and at $198, you're basically robbing Sony blind! They offer great sound with reliable active noise cancellation, making them an ideal choice for anyone who needs a reliable audio companion. The 30 hours of battery life also make them a worthy choice.<a class="view-deal button" href="https://www.amazon.com/Sony-WH-1000XM5-Canceling-Headphones-Optimizer/dp/B0DDV3FRHR" target="_blank" rel="nofollow" data-dimension112="acf8ef08-7035-4828-aa11-53165c530278" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$198">View Deal</a></p></div><p>The Sony WH-1000XM5 headphones are available in four different colors, including Smoky Pink (which I use and love), Black, Midnight Blue and Silver, so there's an option for everyone. </p><p>But when there’s a new version available, why would you buy the old model? Trust me, I hear ya — but the two sets of cans actually have a lot in common. I mean, they’re both some of the <a href="https://www.tomsguide.com/us/best-noise-cancelling-headphones,review-5566.html">best noise-canceling headphones</a> you can buy. One's just a little bit newer.</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:3755px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="unoTGyYcy8HsYM7ErciPsA" name="IMG_2839 (2).JPG" alt="Sony XM5 in there fold-flat case" src="https://cdn.mos.cms.futurecdn.net/unoTGyYcy8HsYM7ErciPsA-1920-80.jpg" mos="" align="middle" fullscreen="" width="3755" height="2112" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You get the same 30-hour battery life, and the sound profile isn't drastically different either. </p><p>If you want the comfort and stunning sound profile that comes with owning a pair of Sony’s flagship headphones — but are also conscious about your budget — then picking up the previous model is the best option.</p><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-audio-deals"><span>We're tracking all the best Prime Day audio deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Audio&min_discount_ratio=0.95&sort=date_desc&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function 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 { 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 (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';          }        }        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 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');                    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, 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.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[ Portable fans are this summer's must-have gadget, and here are 10 fans you can still buy ]]></title>
                                                                                                <dc:content><![CDATA[ <p>'Tis the season. No, not <em>that</em> season. I meant, 'tis the season for being absolutely sweltering, not getting any sleep, and being thoroughly unproductive. I live in the south west of England, and today it's going to hit 38 degrees. So, unsurprisingly, handheld fans are well and truly in.</p><p>As such, I've rounded up a few of my favourites here, in the likely scenario that you're searching for a new one. Happily, Amazon's <a href="https://www.tomsguide.com/live/news/savings-squad-prime-day-live-blog-amazon-best-buy-walmart">Prime Day deals</a> are helping out a little, with discounts on many fans at Amazon, although there are some deals at other retailers, like B&Q/DIY.</p><p>If you need a new fan, keep reading!</p><h2 class="article-body__section" id="section-9-portable-fan-deals-to-keep-you-cool-this-summer"><span>9 portable fan deals to keep you cool this summer</span></h2>        <div class="featured_product_block featured_block_horizontal" data-id="c85d7b16-ff85-499b-820f-4e53fe3ea8ee">            <a href="https://www.amazon.co.uk/REON-POCKET-Sensing-Wearable-Cooler/dp/B0CXCDZ1FW" data-model-name="Sony Reon Pocket 5" 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/JUgqmqkTCXFhbJN7wsG2AX.jpg" alt="Sony Reon Pocket 5 Sensing Kit, Wearable Cooler, Neck Cooler, Wearable Warmer, Neck Heater, Neck Warmer, Thermal Device, Portable Air Conditioner, Personal Cooling Device"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>REON POCKET</div>                                        <div class="featured__title">Sony Reon Pocket 5</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>The REON Pocket is one of our favourite cooling products, which cools you down using its thermal plates. Attach it to your neck for full body cooling.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="968d0903-24ba-4abe-bd47-3a2657e99ca4">            <a href="https://www.amazon.co.uk/Shark-High-Speed-Attachments-Travel-Friendly-FA022UKPK/dp/B0GKPQ4YC4/" data-model-name="Shark Chillpill 3-In-1" 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/3XqXmiqLADEtj365eZ87cd.jpg" alt="Shark Chillpill 3-In-1 Personal High-Speed Fan, Ultra-Fine Mist and Instachill System, Includes 3 Cooling Attachments, Travel-Friendly Design, Dragon Fruit Fa022ukpk"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shark</div>                                        <div class="featured__title">Shark Chillpill 3-In-1</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Probably the most in-demand fan this season, the Chillpill is all over social. It combines a typical fan with a mist function and a cooling contact plate. It's expensive, but can you really put a price on staying comfortable?</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="d689d940-576b-4464-b70d-3cb6b5fe6662">            <a href="https://www.amazon.co.uk/Warmco-Portable-Personal-Adjustable-Hands-Free/dp/B0GS6NZPJB" data-model-name="Warmco Portable Neck Fan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/G86bA47Co4djTrVoczv3eS.jpg" alt="Warmco Portable Neck Fan, 5200mah Battery Powered Personal Fan With Led Display, Surround Cooling & 60° Adjustable Airflow, Strong Wind, Hands-Free Fan for Travel, Camping, Office"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Warmco</div>                                        <div class="featured__title">Warmco Portable Neck Fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Warmco is hitting the Amazon sales hard this Prime Day, including this portable neck fan. Wear it around your neck for full body cooling, as you can pitch the fans up and down to cool your face and body.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="7eb3a109-dd2d-4927-8e1a-9118b5d7c2af">            <a href="https://www.amazon.co.uk/Warmco-Portable-Handheld-Adjustable-Rechargeable/dp/B0F7KB3795/" data-model-name="Warmco Portable Handheld Fan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/5AnjM6wZsHXvXrJYSVCqBR.jpg" alt="Warmco Portable Handheld Fan 5000mah, 1-100 Speed Stepless Turbo Fan (15000 Rpm), Small Personal Mini Fan With Led Display, Usb Rechargeable for Travel, Commuting, Office, Makeup & Eyelash Dry"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Warmco</div>                                        <div class="featured__title">Warmco Portable Handheld Fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>I actually own this fan, and it's been a life-saver while I'm out and about, especially on trips. Its very powerful, but at its lower settings will last a good few hours (and still provide plenty of cooling).</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="869e5cfa-7d9f-40b0-9f28-1c95c477c84b">            <a href="https://www.amazon.co.uk/Portable-Handheld-Adjustable-Rechargeable-Essentials/dp/B0F6LLP9CR" data-model-name="Epicka Portable Handheld Fan" 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/3B2GzQuVpCFy6MLBVdLhUS.jpg" alt="Epicka Portable Handheld Fan, Speed(1-100) Adjustable Turbo Mini Fan,6800mah Battery Rechargeable Foldable Fan With Cooling Plate,digital Display,summer Outdoor Travel Essentials for Women Men - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>EPICKA</div>                                        <div class="featured__title">Epicka Portable Handheld Fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Another handheld turbo jet fan, this one has a useful folding battery compartment, allowing you to prop it up and use it as a mini desk fan.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="730e4897-3ffc-4182-8077-bf1d8370ffb7">            <a href="https://www.amazon.co.uk/JISULIFE-Handheld-Powerbank-Rechargeable-Fan-White/dp/B09PDMZKQW" data-model-name="Jisulife Handheld Fan" 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/2NLifEnwYzGkgB5KAvFjYW.jpg" alt="Jisulife Handheld Fan With 4500 Mah Powerbank Max 37 Hours Runtime, Personal Portable Fan Pocket Usb Powered Folding and Mini Rechargeable Quiet -White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>JISULIFE</div>                                        <div class="featured__title">Jisulife Handheld Fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>A classic handheld fan — no frills but probably just enough chills. And for under £15, this is a cost-effective solution.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="1714cf79-7867-4a87-8710-c1123b8be644">            <a href="https://www.amazon.co.uk/Warmco-10000mAh-Portable-Ultra-thin-Operated/dp/B0DNM5H1KP" data-model-name="Warmco 10000mah Portable Desk Fan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/CRs5dnY2d9X8QJeTkjf9ZQ.jpg" alt="Warmco 10000mah Portable Desk Fan 21cm, Up to 24h Runtime, Ultra-Slim Quiet Rechargeable Battery Operated Fan With Digital Display for Travel, Camping, Office, Bedroom, Cruise Essentials"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Warmco</div>                                        <div class="featured__title">Warmco 10000mah Portable Desk Fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Speaking of mini desk fans, Warmco has another deal on this USB-C desk fan which will be perfect for keeping cool at your workstation.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="0323c5f5-3f53-4a06-b1a9-481318e57a1f">            <a href="https://www.amazon.co.uk/Warmco-Control-Powered-Battery-Personal/dp/B0G4BVTN9J" data-model-name="Warmco Usb Desk Fan With Touch Control" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/89oMnw4v5HcBLA397NJjrP.jpg" alt="Warmco Usb Desk Fan With Touch Control, (usb Powered Only, No Battery) Small Personal Desktop Fan, Usb-A & Usb-C Plug, 3 Speeds Strong Airflow, Quiet Table Fans for Home Office Dorm"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Warmco</div>                                        <div class="featured__title">Warmco Usb Desk Fan With Touch Control</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Alternatively, if you're short on space or want something a little less (dare I say it) ugly than the fan above, there's also this sompact touch controlled Warmco fan on sale.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="c6455eaf-f8d5-4c29-81eb-c889766464c6">            <a href="https://www.diy.com/departments/geepas-portable-neck-fan-3-speed-usb-charging-6hr-run-time-2000mah/6294015578065_BQ.prd" data-model-name="Neck fan" 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/ue7A9gotTBLWi8rN7vRxxC.jpg" alt="Geepas neck fan white background"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Geepas</div>                                        <div class="featured__title">Neck fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Another neck fan here, currently on sale at B&Q, it has three cooling modes and promises up to 6 hours of cooling.</p></p>                </div>                            </div>        </div><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=fans&min_discount_ratio=0.95&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function 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 { 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 (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';          }        }        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 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');                    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, 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.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> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tech/portable-fans-are-this-summers-must-have-gadget-and-here-are-the-fans-you-can-still-buy</link>
                                                                            <description>
                            <![CDATA[ Handheld fans are saving people from the sweltering heat right now, and these are the ones we recommend buying today. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">C2DtwHdCgvmmCsVvYA3N9S</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/aNYcDXRpJEm5vzAqBGQAw4-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 25 Jun 2026 11:10:15 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Tech]]></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-320-70.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/aNYcDXRpJEm5vzAqBGQAw4-1920-80.jpg">
                                                            <media:credit><![CDATA[Sony, Shark, Warmco, Geepas, Epicka, Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A collage of handheld fans on a blue background]]></media:description>                                                            <media:text><![CDATA[A collage of handheld fans on a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[A collage of handheld fans on a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/aNYcDXRpJEm5vzAqBGQAw4-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>'Tis the season. No, not <em>that</em> season. I meant, 'tis the season for being absolutely sweltering, not getting any sleep, and being thoroughly unproductive. I live in the south west of England, and today it's going to hit 38 degrees. So, unsurprisingly, handheld fans are well and truly in.</p><p>As such, I've rounded up a few of my favourites here, in the likely scenario that you're searching for a new one. Happily, Amazon's <a href="https://www.tomsguide.com/live/news/savings-squad-prime-day-live-blog-amazon-best-buy-walmart">Prime Day deals</a> are helping out a little, with discounts on many fans at Amazon, although there are some deals at other retailers, like B&Q/DIY.</p><p>If you need a new fan, keep reading!</p><h2 class="article-body__section" id="section-9-portable-fan-deals-to-keep-you-cool-this-summer"><span>9 portable fan deals to keep you cool this summer</span></h2>        <div class="featured_product_block featured_block_horizontal" data-id="c85d7b16-ff85-499b-820f-4e53fe3ea8ee">            <a href="https://www.amazon.co.uk/REON-POCKET-Sensing-Wearable-Cooler/dp/B0CXCDZ1FW" data-model-name="Sony Reon Pocket 5" 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/JUgqmqkTCXFhbJN7wsG2AX.jpg" alt="Sony Reon Pocket 5 Sensing Kit, Wearable Cooler, Neck Cooler, Wearable Warmer, Neck Heater, Neck Warmer, Thermal Device, Portable Air Conditioner, Personal Cooling Device"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>REON POCKET</div>                                        <div class="featured__title">Sony Reon Pocket 5</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>The REON Pocket is one of our favourite cooling products, which cools you down using its thermal plates. Attach it to your neck for full body cooling.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="968d0903-24ba-4abe-bd47-3a2657e99ca4">            <a href="https://www.amazon.co.uk/Shark-High-Speed-Attachments-Travel-Friendly-FA022UKPK/dp/B0GKPQ4YC4/" data-model-name="Shark Chillpill 3-In-1" 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/3XqXmiqLADEtj365eZ87cd.jpg" alt="Shark Chillpill 3-In-1 Personal High-Speed Fan, Ultra-Fine Mist and Instachill System, Includes 3 Cooling Attachments, Travel-Friendly Design, Dragon Fruit Fa022ukpk"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shark</div>                                        <div class="featured__title">Shark Chillpill 3-In-1</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Probably the most in-demand fan this season, the Chillpill is all over social. It combines a typical fan with a mist function and a cooling contact plate. It's expensive, but can you really put a price on staying comfortable?</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="d689d940-576b-4464-b70d-3cb6b5fe6662">            <a href="https://www.amazon.co.uk/Warmco-Portable-Personal-Adjustable-Hands-Free/dp/B0GS6NZPJB" data-model-name="Warmco Portable Neck Fan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/G86bA47Co4djTrVoczv3eS.jpg" alt="Warmco Portable Neck Fan, 5200mah Battery Powered Personal Fan With Led Display, Surround Cooling & 60° Adjustable Airflow, Strong Wind, Hands-Free Fan for Travel, Camping, Office"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Warmco</div>                                        <div class="featured__title">Warmco Portable Neck Fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Warmco is hitting the Amazon sales hard this Prime Day, including this portable neck fan. Wear it around your neck for full body cooling, as you can pitch the fans up and down to cool your face and body.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="7eb3a109-dd2d-4927-8e1a-9118b5d7c2af">            <a href="https://www.amazon.co.uk/Warmco-Portable-Handheld-Adjustable-Rechargeable/dp/B0F7KB3795/" data-model-name="Warmco Portable Handheld Fan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/5AnjM6wZsHXvXrJYSVCqBR.jpg" alt="Warmco Portable Handheld Fan 5000mah, 1-100 Speed Stepless Turbo Fan (15000 Rpm), Small Personal Mini Fan With Led Display, Usb Rechargeable for Travel, Commuting, Office, Makeup & Eyelash Dry"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Warmco</div>                                        <div class="featured__title">Warmco Portable Handheld Fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>I actually own this fan, and it's been a life-saver while I'm out and about, especially on trips. Its very powerful, but at its lower settings will last a good few hours (and still provide plenty of cooling).</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="869e5cfa-7d9f-40b0-9f28-1c95c477c84b">            <a href="https://www.amazon.co.uk/Portable-Handheld-Adjustable-Rechargeable-Essentials/dp/B0F6LLP9CR" data-model-name="Epicka Portable Handheld Fan" 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/3B2GzQuVpCFy6MLBVdLhUS.jpg" alt="Epicka Portable Handheld Fan, Speed(1-100) Adjustable Turbo Mini Fan,6800mah Battery Rechargeable Foldable Fan With Cooling Plate,digital Display,summer Outdoor Travel Essentials for Women Men - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>EPICKA</div>                                        <div class="featured__title">Epicka Portable Handheld Fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Another handheld turbo jet fan, this one has a useful folding battery compartment, allowing you to prop it up and use it as a mini desk fan.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="730e4897-3ffc-4182-8077-bf1d8370ffb7">            <a href="https://www.amazon.co.uk/JISULIFE-Handheld-Powerbank-Rechargeable-Fan-White/dp/B09PDMZKQW" data-model-name="Jisulife Handheld Fan" 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/2NLifEnwYzGkgB5KAvFjYW.jpg" alt="Jisulife Handheld Fan With 4500 Mah Powerbank Max 37 Hours Runtime, Personal Portable Fan Pocket Usb Powered Folding and Mini Rechargeable Quiet -White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>JISULIFE</div>                                        <div class="featured__title">Jisulife Handheld Fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>A classic handheld fan — no frills but probably just enough chills. And for under £15, this is a cost-effective solution.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="1714cf79-7867-4a87-8710-c1123b8be644">            <a href="https://www.amazon.co.uk/Warmco-10000mAh-Portable-Ultra-thin-Operated/dp/B0DNM5H1KP" data-model-name="Warmco 10000mah Portable Desk Fan" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/CRs5dnY2d9X8QJeTkjf9ZQ.jpg" alt="Warmco 10000mah Portable Desk Fan 21cm, Up to 24h Runtime, Ultra-Slim Quiet Rechargeable Battery Operated Fan With Digital Display for Travel, Camping, Office, Bedroom, Cruise Essentials"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Warmco</div>                                        <div class="featured__title">Warmco 10000mah Portable Desk Fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Speaking of mini desk fans, Warmco has another deal on this USB-C desk fan which will be perfect for keeping cool at your workstation.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="0323c5f5-3f53-4a06-b1a9-481318e57a1f">            <a href="https://www.amazon.co.uk/Warmco-Control-Powered-Battery-Personal/dp/B0G4BVTN9J" data-model-name="Warmco Usb Desk Fan With Touch Control" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/89oMnw4v5HcBLA397NJjrP.jpg" alt="Warmco Usb Desk Fan With Touch Control, (usb Powered Only, No Battery) Small Personal Desktop Fan, Usb-A & Usb-C Plug, 3 Speeds Strong Airflow, Quiet Table Fans for Home Office Dorm"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Warmco</div>                                        <div class="featured__title">Warmco Usb Desk Fan With Touch Control</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Alternatively, if you're short on space or want something a little less (dare I say it) ugly than the fan above, there's also this sompact touch controlled Warmco fan on sale.</p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_horizontal" data-id="c6455eaf-f8d5-4c29-81eb-c889766464c6">            <a href="https://www.diy.com/departments/geepas-portable-neck-fan-3-speed-usb-charging-6hr-run-time-2000mah/6294015578065_BQ.prd" data-model-name="Neck fan" 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/ue7A9gotTBLWi8rN7vRxxC.jpg" alt="Geepas neck fan white background"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Geepas</div>                                        <div class="featured__title">Neck fan</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>Another neck fan here, currently on sale at B&Q, it has three cooling modes and promises up to 6 hours of cooling.</p></p>                </div>                            </div>        </div><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=fans&min_discount_ratio=0.95&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function 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 { 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 (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';          }        }        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 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');                    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, 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.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[ I found the 4 best OLED Prime Day TV deals starting from $599 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Take it from someone who's been covering the TV business for over ten years: The majority of sets that go on sale during Prime Day are not worth buying. </p><p>However, OLED TVs from Samsung, Sony and LG are top of their class for a reason. Thanks to OLED's incredible display technology, they offer perfect black levels, pixel-level contrast control and a host of other performance-related benefits. While OLED TVs may be pricier than most LCD TVs, they make up for it when it comes to picture quality.</p><p>These are my four favorite options for under $1,000 — and, for more options, be sure to check out our <a href="https://www.tomsguide.com/tvs/live/best-prime-day-tv-deals-2026-live-sales-on-affordable-oleds-qleds-and-more">Amazon Prime Day TV sales live blog</a> to see all the latest deals.</p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><a href="https://target.georiot.com/Proxy.ashx?tsid=45724&GR_URL=https%3A%2F%2Famazon.com%2Fgp%2Fbrowse.html%3Frw_useCurrentProtocol%3D1%26node%3D172659%26tag%3Dftr-tomsguide-us-20%26ascsubtag%3Dtomsguide-us-3841752069760675880-20" target="_blank" rel="nofollow"><strong>shop TV deals from $99 at Amazon</strong></a></li><li><strong>LG 48” B5 4K OLED TV: </strong><a href="https://www.bestbuy.com/product/lg-48-class-b5-series-oled-ai-4k-uhd-smart-webos-tv-2025/JJ8VPZTK9F" target="_blank" rel="nofollow"><strong>was $649 now $599 @ Best Buy</strong></a></li><li><strong>Samsung 48” S90F 4K OLED TV: </strong><a href="https://www.amazon.com/SAMSUNG-48-Inch-Processor-Upscaling-Xcelerator/dp/B0DXN1BFQ4" target="_blank" rel="nofollow"><strong>was $1,397 now $797 @ Amazon</strong></a></li><li><strong>LG 42” C5 4K OLED TV: </strong><a href="https://www.amazon.com/LG-Upscaling-Filmmaker-Orchestra-OLED65C5PUA/dp/B0DYQMWSKG" target="_blank" rel="nofollow"><strong>was $999 now $899 @ Amazon</strong></a></li><li><strong>Sony 55” XR8B 4K OLED TV: </strong><a href="https://www.amazon.com/Sony-Exclusive-Features-PlayStation%C2%AE-K-55XR8B/dp/B0FHX84W4X/" target="_blank" rel="nofollow"><strong>was $1,499 now $998 @ Amazon</strong></a></li></ul><h3 class="article-body__section" id="section-my-favorite-prime-day-oled-deals"><span>My favorite Prime Day OLED deals</span></h3><h2 id="1-lg-b5">1. LG B5</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="7GgNL7uAWkPJJpmXRMRLqa" name="LG B5--07" alt="LG B5 OLED TV on a side table" src="https://cdn.mos.cms.futurecdn.net/7GgNL7uAWkPJJpmXRMRLqa-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The 48-inch LG B5 is one of the most affordable ways to bring OLED technology into your home, but in order to get it, you need to leave Amazon altogether. The 48-inch version of LG's entry-level B5 is a Best Buy-exclusive model, and right now, it's back down to its Black Friday price of just $599.</p><p>Higher-end LG OLEDs will net you better brightness than the B5, you're still getting OLED performance with LG's entry-level set, and that advantage goes a long way. Plus, the B5 offers many of the same bells and whistles as those higher-end sets.</p><p>Gamers are getting basically everything they need for current-gen consoles, as the B5 supports 4K gaming at 120Hz and comes with Auto Low Latency Mode (ALLM), Variable Refresh Rate (VRR), AMD FreeSync and G-Sync compatibility. Crucially, all four of the LG B5's HDMI inputs offer <a href="https://www.tomsguide.com/tvs/hdmi-2-1-vs-hdmi-2-2-biggest-differences-explained-and-when-you-should-upgrade">HDMI 2.1 compatibility</a>. For a $599 48-inch TV, that's tough to come by.</p><div class="product"><a data-dimension112="87dde401-35ce-4014-9298-083ee534a057" data-action="Deal Block" data-label="48" B5 4K OLED TV" data-dimension48="48" B5 4K OLED TV" data-dimension25="$599" href="https://www.bestbuy.com/site/lg-48-class-b5-series-oled-ai-4k-uhd-smart-webos-tv-2025/6635292.p?skuId=6635292" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3000px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="AL8CEEwZA5Bk4xbAfk5U2U" name="LG-B5-OLED-TV-Deal-block-16-by-9" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/AL8CEEwZA5Bk4xbAfk5U2U-1920-80.jpg" mos="" align="middle" fullscreen="" width="3000" height="1688" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/site/lg-48-class-b5-series-oled-ai-4k-uhd-smart-webos-tv-2025/6635292.p?skuId=6635292" target="_blank" rel="nofollow" data-dimension112="87dde401-35ce-4014-9298-083ee534a057" data-action="Deal Block" data-label='48" B5 4K OLED TV' data-dimension48='48" B5 4K OLED TV' data-dimension25="$599">48" B5 4K OLED TV: was $1299 now $599</a></strong><br>The B5 is LG's most affordable OLED from 2025, but don't let that fool you into thinking that it's not a terrific TV for folks looking to save money on an OLED. In addition to supporting 4K gaming at 120Hz, the B5 also supports Dolby Vision HDR and costs significantly less than its higher-end counterparts.<a class="view-deal button" href="https://www.bestbuy.com/site/lg-48-class-b5-series-oled-ai-4k-uhd-smart-webos-tv-2025/6635292.p?skuId=6635292" target="_blank" rel="nofollow" data-dimension112="87dde401-35ce-4014-9298-083ee534a057" data-action="Deal Block" data-label="48" B5 4K OLED TV" data-dimension48="48" B5 4K OLED TV" data-dimension25="$599">View Deal</a></p></div><h2 id="2-samsung-s90f">2. Samsung S90F</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.15%;"><img id="UXjjPHnVwqGPU8WsoCedJf" name="Samsung S90F--1-LEDE" alt="Samsung S90F" src="https://cdn.mos.cms.futurecdn.net/UXjjPHnVwqGPU8WsoCedJf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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 S90F sits between Samsung's entry-level OLED, the S85F, and the flagship-level <a href="https://www.tomsguide.com/tvs/4k-tvs/samsung-s95f-oled-tv-review">Samsung S95F</a>. This means that it offers better performance than a more-affordable OLED TV (like the aforementioned LG B5), but still comes in at a friendly price point.</p><p>The S90F supports 4K gaming at 120Hz (and up to 144Hz when connected to a gaming PC) across all four of its HDMI 2.1-compatible inputs. FreeSync support and G-Sync compatibility are here, as well as Samsung's Gaming Hub software suite.</p><p>Several sizes in the series are on sale this week, but my favorite deal involves the 48-inch S90F. It's currently about $100 cheaper than my next pick, the 42-inch LG C5, despite offering a bigger screen.</p><div class="product"><a data-dimension112="e7843bc5-ffc9-43cf-9df7-d41d29cce35b" data-action="Deal Block" data-label="48" S90F 4K OLED TV" data-dimension48="48" S90F 4K OLED TV" data-dimension25="$797" href="https://www.amazon.com/SAMSUNG-48-Inch-Processor-Upscaling-Xcelerator/dp/B0DXN1BFQ4" 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:63.07%;"><img id="YBxrB7t9rpdaSLMFUdxqwE" name="81a7MINnI9L._AC_SL1500_" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/YBxrB7t9rpdaSLMFUdxqwE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="946" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/SAMSUNG-48-Inch-Processor-Upscaling-Xcelerator/dp/B0DXN1BFQ4" target="_blank" rel="nofollow" data-dimension112="e7843bc5-ffc9-43cf-9df7-d41d29cce35b" data-action="Deal Block" data-label='48" S90F 4K OLED TV' data-dimension48='48" S90F 4K OLED TV' data-dimension25="$797">48" S90F 4K OLED TV: was $1397 now $797</a></strong><br>Thanks to an especially punchy picture for the price, Samsung's mid-range OLED is a fantastic pick for living rooms with above-average brightness. It's also jam-packed with features for streaming and gaming. I find the 48-inch model to have the most enticing sale price this Prime Day, but the 65-inch version is on sale for an attractive price, too.<br><strong>65-inch S90F: </strong><a href="https://www.amazon.com/Samsung-QN65S90FAFXZA-Smart-Dolby-Protection/dp/B0F6Q5B6XB/" target="_blank" rel="nofollow">$1,197 @ Amazon</a><a class="view-deal button" href="https://www.amazon.com/SAMSUNG-48-Inch-Processor-Upscaling-Xcelerator/dp/B0DXN1BFQ4" target="_blank" rel="nofollow" data-dimension112="e7843bc5-ffc9-43cf-9df7-d41d29cce35b" data-action="Deal Block" data-label="48" S90F 4K OLED TV" data-dimension48="48" S90F 4K OLED TV" data-dimension25="$797">View Deal</a></p></div><h2 id="3-lg-c5">3. LG C5</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="h9dbeJK2VNhVSDxuvbre2c" name="LG C5-06" alt="Spider-Man: Across the Spider-Verse on a LG C5 OLED TV" src="https://cdn.mos.cms.futurecdn.net/h9dbeJK2VNhVSDxuvbre2c-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The LG C5 comes from a long line of mid-range OLED TVs whose value is almost as impressive as its performance. In fact, it's the only TV on this list to have received our coveted, five-star rating.</p><p>You can read more about this stunning TV in our <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c5-oled-tv-review">LG C5 review</a>, but the basic gist is that it's practically overflowing with value, particularly once you factor in its Prime Day sale price. Unlike the previously mentioned Samsung S90F, the C5 is a mid-range OLED that includes Dolby Vision support.</p><p>The 42-inch C5 also arrives with just about every feature you could want for gaming and streaming. Its thorough selection of gaming features is a big reason why I keep recommending it to folks in the market for a 42-inch gaming monitor.</p><div class="product"><a data-dimension112="91a95552-d71b-415e-8524-23224acb7375" data-action="Deal Block" data-label="42" C5 4K OLED TV" data-dimension48="42" C5 4K OLED TV" data-dimension25="$899" href="https://www.amazon.com/LG-Upscaling-Filmmaker-Orchestra-OLED65C5PUA/dp/B0DYQMWSKG" 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:60.60%;"><img id="oBaPUFAAexE2Jge2Vinp73" name="91bxFjk187L._AC_SL1500_" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/oBaPUFAAexE2Jge2Vinp73-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="909" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/LG-Upscaling-Filmmaker-Orchestra-OLED65C5PUA/dp/B0DYQMWSKG" target="_blank" rel="nofollow" data-dimension112="91a95552-d71b-415e-8524-23224acb7375" data-action="Deal Block" data-label='42" C5 4K OLED TV' data-dimension48='42" C5 4K OLED TV' data-dimension25="$899">42" C5 4K OLED TV: was $999 now $899</a></strong><br>We showered this OLED TV with praise for its sensational performance, its class-leading selection of features and its approachable price point. The C5 delivers all of the benefits we've come to expect from OLED displays, but its design, software and overall experience is the result of LG's excellent engineering. This sale price involves the 42-inch C5, but the 65-inch version is on sale, as well.<br><strong>65-inch C5: </strong><a href="https://www.amazon.com/LG-Upscaling-Filmmaker-Orchestra-OLED65C5PUA/dp/B0DYQM4BDB/" target="_blank" rel="nofollow">$1,197 @ Amazon</a><a class="view-deal button" href="https://www.amazon.com/LG-Upscaling-Filmmaker-Orchestra-OLED65C5PUA/dp/B0DYQMWSKG" target="_blank" rel="nofollow" data-dimension112="91a95552-d71b-415e-8524-23224acb7375" data-action="Deal Block" data-label="42" C5 4K OLED TV" data-dimension48="42" C5 4K OLED TV" data-dimension25="$899">View Deal</a></p></div><h2 id="4-sony-xr8b">4. Sony XR8B</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="ayQPFVZHWwy4KNM5Zh3kRR" name="Sony-Bravia-XR8B-OLED-TV-lifestyle" alt="The Sony Bravia XR8B OLED TV displaying a colorful, abstract picture in a monochromatic living room" src="https://cdn.mos.cms.futurecdn.net/ayQPFVZHWwy4KNM5Zh3kRR-1920-80.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: Sony)</span></figcaption></figure><p>Among our readers, the Sony XR8B has been a popular pick during Prime Day, Cyber Monday and Black Friday sales events for a couple of years now. Don't let its prolonged shelf life stop you from considering it, though — it's one of the most affordable ways to enjoy a Sony-engineered OLED TV in 2026.</p><p>Sony's much-lauded picture processing is the highlight here. Other OLED TVs may get brighter and offer better color volume, but even Sony's most-affordable OLEDs are driven by some of the best picture processing capabilities the industry has to offer.</p><p>The XR8B comes with plenty of features, too, like support for 4K gaming at 120Hz and the Google TV smart platform.</p><div class="product"><a data-dimension112="0400fcff-3986-4d01-9536-b02c13b88192" data-action="Deal Block" data-label="55" XR8B 4K OLED TV" data-dimension48="55" XR8B 4K OLED TV" data-dimension25="$998" href="https://www.amazon.com/Sony-Exclusive-Features-PlayStation%C2%AE-K-55XR8B/dp/B0FHX84W4X/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="cATN8W9ThdVPgw7uRT8ine" name="Sony-Bravia-XR8B-OLED-TV-16-by-9-deal-block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/cATN8W9ThdVPgw7uRT8ine-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Sony-Exclusive-Features-PlayStation%C2%AE-K-55XR8B/dp/B0FHX84W4X/" target="_blank" rel="nofollow" data-dimension112="0400fcff-3986-4d01-9536-b02c13b88192" data-action="Deal Block" data-label='55" XR8B 4K OLED TV' data-dimension48='55" XR8B 4K OLED TV' data-dimension25="$998">55" XR8B 4K OLED TV: was $1499 now $998</a></strong><br>The 55-inch XR8B blends Sony's top-notch picture processing with an array of features for streaming and gaming. This includes built-in Google TV and support for 4K gaming at 120Hz.<a class="view-deal button" href="https://www.amazon.com/Sony-Exclusive-Features-PlayStation%C2%AE-K-55XR8B/dp/B0FHX84W4X/" target="_blank" rel="nofollow" data-dimension112="0400fcff-3986-4d01-9536-b02c13b88192" data-action="Deal Block" data-label="55" XR8B 4K OLED TV" data-dimension48="55" XR8B 4K OLED TV" data-dimension25="$998">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/tvs/live/best-prime-day-tv-deals-2026-live-sales-on-affordable-oleds-qleds-and-more">I've been reviewing TVs for over a decade — these are the 38 Prime Day TV deals worth your time</a></li><li><a href="https://www.tomsguide.com/gaming/nintendo/best-nintendo-switch-2-prime-day-deals-17-discounts-on-bundles-games-and-accessories-id-buy-with-my-own-money">I'm tracking all the Nintendo Switch 2 Prime Day deals — and these are the 17 discounts I'd actually get</a></li><li><a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Sony’s new Bravia 7 II has a secret weapon that makes it so good</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/oled-tvs/i-found-the-4-best-oled-prime-day-tv-deals-starting-from-usd599</link>
                                                                            <description>
                            <![CDATA[ I sorted through dozens of listings to find the best Prime Day OLED TV deals from brands like LG, Samsung and Sony. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">niH7MizuGw5s6mkTxN2w7K</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/3aZqbnmRvBypizqrBGEuq3-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 24 Jun 2026 09:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[OLED TVs]]></category>
                                                    <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ michael.desjardin@futurenet.com (Michael Desjardin) ]]></author>                    <dc:creator><![CDATA[ Michael Desjardin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/esytxRqW9GNVZNVCf7BFyV-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael Desjardin is a Senior Editor for TVs at Tom&#039;s Guide. He&#039;s been testing and tinkering with TVs professionally for over a decade, previously for Reviewed and USA Today. Michael studied media production and screenwriting at Emerson College. He loves cooking, zoning out to ambient music, and getting way too invested in the Red Sox. He considers himself living proof that TV doesn&#039;t necessarily rot your brain.&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/3aZqbnmRvBypizqrBGEuq3-1920-80.jpg">
                                                            <media:credit><![CDATA[Samsung]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Samsung S90F OLED TV displaying colorful, geographical formations in a well-lit living room]]></media:description>                                                            <media:text><![CDATA[The Samsung S90F OLED TV displaying colorful, geographical formations in a well-lit living room]]></media:text>
                                <media:title type="plain"><![CDATA[The Samsung S90F OLED TV displaying colorful, geographical formations in a well-lit living room]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/3aZqbnmRvBypizqrBGEuq3-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Take it from someone who's been covering the TV business for over ten years: The majority of sets that go on sale during Prime Day are not worth buying. </p><p>However, OLED TVs from Samsung, Sony and LG are top of their class for a reason. Thanks to OLED's incredible display technology, they offer perfect black levels, pixel-level contrast control and a host of other performance-related benefits. While OLED TVs may be pricier than most LCD TVs, they make up for it when it comes to picture quality.</p><p>These are my four favorite options for under $1,000 — and, for more options, be sure to check out our <a href="https://www.tomsguide.com/tvs/live/best-prime-day-tv-deals-2026-live-sales-on-affordable-oleds-qleds-and-more">Amazon Prime Day TV sales live blog</a> to see all the latest deals.</p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><a href="https://target.georiot.com/Proxy.ashx?tsid=45724&GR_URL=https%3A%2F%2Famazon.com%2Fgp%2Fbrowse.html%3Frw_useCurrentProtocol%3D1%26node%3D172659%26tag%3Dftr-tomsguide-us-20%26ascsubtag%3Dtomsguide-us-3841752069760675880-20" target="_blank" rel="nofollow"><strong>shop TV deals from $99 at Amazon</strong></a></li><li><strong>LG 48” B5 4K OLED TV: </strong><a href="https://www.bestbuy.com/product/lg-48-class-b5-series-oled-ai-4k-uhd-smart-webos-tv-2025/JJ8VPZTK9F" target="_blank" rel="nofollow"><strong>was $649 now $599 @ Best Buy</strong></a></li><li><strong>Samsung 48” S90F 4K OLED TV: </strong><a href="https://www.amazon.com/SAMSUNG-48-Inch-Processor-Upscaling-Xcelerator/dp/B0DXN1BFQ4" target="_blank" rel="nofollow"><strong>was $1,397 now $797 @ Amazon</strong></a></li><li><strong>LG 42” C5 4K OLED TV: </strong><a href="https://www.amazon.com/LG-Upscaling-Filmmaker-Orchestra-OLED65C5PUA/dp/B0DYQMWSKG" target="_blank" rel="nofollow"><strong>was $999 now $899 @ Amazon</strong></a></li><li><strong>Sony 55” XR8B 4K OLED TV: </strong><a href="https://www.amazon.com/Sony-Exclusive-Features-PlayStation%C2%AE-K-55XR8B/dp/B0FHX84W4X/" target="_blank" rel="nofollow"><strong>was $1,499 now $998 @ Amazon</strong></a></li></ul><h3 class="article-body__section" id="section-my-favorite-prime-day-oled-deals"><span>My favorite Prime Day OLED deals</span></h3><h2 id="1-lg-b5">1. LG B5</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="7GgNL7uAWkPJJpmXRMRLqa" name="LG B5--07" alt="LG B5 OLED TV on a side table" src="https://cdn.mos.cms.futurecdn.net/7GgNL7uAWkPJJpmXRMRLqa-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The 48-inch LG B5 is one of the most affordable ways to bring OLED technology into your home, but in order to get it, you need to leave Amazon altogether. The 48-inch version of LG's entry-level B5 is a Best Buy-exclusive model, and right now, it's back down to its Black Friday price of just $599.</p><p>Higher-end LG OLEDs will net you better brightness than the B5, you're still getting OLED performance with LG's entry-level set, and that advantage goes a long way. Plus, the B5 offers many of the same bells and whistles as those higher-end sets.</p><p>Gamers are getting basically everything they need for current-gen consoles, as the B5 supports 4K gaming at 120Hz and comes with Auto Low Latency Mode (ALLM), Variable Refresh Rate (VRR), AMD FreeSync and G-Sync compatibility. Crucially, all four of the LG B5's HDMI inputs offer <a href="https://www.tomsguide.com/tvs/hdmi-2-1-vs-hdmi-2-2-biggest-differences-explained-and-when-you-should-upgrade">HDMI 2.1 compatibility</a>. For a $599 48-inch TV, that's tough to come by.</p><div class="product"><a data-dimension112="87dde401-35ce-4014-9298-083ee534a057" data-action="Deal Block" data-label="48" B5 4K OLED TV" data-dimension48="48" B5 4K OLED TV" data-dimension25="$599" href="https://www.bestbuy.com/site/lg-48-class-b5-series-oled-ai-4k-uhd-smart-webos-tv-2025/6635292.p?skuId=6635292" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3000px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="AL8CEEwZA5Bk4xbAfk5U2U" name="LG-B5-OLED-TV-Deal-block-16-by-9" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/AL8CEEwZA5Bk4xbAfk5U2U-1920-80.jpg" mos="" align="middle" fullscreen="" width="3000" height="1688" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/site/lg-48-class-b5-series-oled-ai-4k-uhd-smart-webos-tv-2025/6635292.p?skuId=6635292" target="_blank" rel="nofollow" data-dimension112="87dde401-35ce-4014-9298-083ee534a057" data-action="Deal Block" data-label='48" B5 4K OLED TV' data-dimension48='48" B5 4K OLED TV' data-dimension25="$599">48" B5 4K OLED TV: was $1299 now $599</a></strong><br>The B5 is LG's most affordable OLED from 2025, but don't let that fool you into thinking that it's not a terrific TV for folks looking to save money on an OLED. In addition to supporting 4K gaming at 120Hz, the B5 also supports Dolby Vision HDR and costs significantly less than its higher-end counterparts.<a class="view-deal button" href="https://www.bestbuy.com/site/lg-48-class-b5-series-oled-ai-4k-uhd-smart-webos-tv-2025/6635292.p?skuId=6635292" target="_blank" rel="nofollow" data-dimension112="87dde401-35ce-4014-9298-083ee534a057" data-action="Deal Block" data-label="48" B5 4K OLED TV" data-dimension48="48" B5 4K OLED TV" data-dimension25="$599">View Deal</a></p></div><h2 id="2-samsung-s90f">2. Samsung S90F</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.15%;"><img id="UXjjPHnVwqGPU8WsoCedJf" name="Samsung S90F--1-LEDE" alt="Samsung S90F" src="https://cdn.mos.cms.futurecdn.net/UXjjPHnVwqGPU8WsoCedJf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" 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 S90F sits between Samsung's entry-level OLED, the S85F, and the flagship-level <a href="https://www.tomsguide.com/tvs/4k-tvs/samsung-s95f-oled-tv-review">Samsung S95F</a>. This means that it offers better performance than a more-affordable OLED TV (like the aforementioned LG B5), but still comes in at a friendly price point.</p><p>The S90F supports 4K gaming at 120Hz (and up to 144Hz when connected to a gaming PC) across all four of its HDMI 2.1-compatible inputs. FreeSync support and G-Sync compatibility are here, as well as Samsung's Gaming Hub software suite.</p><p>Several sizes in the series are on sale this week, but my favorite deal involves the 48-inch S90F. It's currently about $100 cheaper than my next pick, the 42-inch LG C5, despite offering a bigger screen.</p><div class="product"><a data-dimension112="e7843bc5-ffc9-43cf-9df7-d41d29cce35b" data-action="Deal Block" data-label="48" S90F 4K OLED TV" data-dimension48="48" S90F 4K OLED TV" data-dimension25="$797" href="https://www.amazon.com/SAMSUNG-48-Inch-Processor-Upscaling-Xcelerator/dp/B0DXN1BFQ4" 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:63.07%;"><img id="YBxrB7t9rpdaSLMFUdxqwE" name="81a7MINnI9L._AC_SL1500_" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/YBxrB7t9rpdaSLMFUdxqwE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="946" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/SAMSUNG-48-Inch-Processor-Upscaling-Xcelerator/dp/B0DXN1BFQ4" target="_blank" rel="nofollow" data-dimension112="e7843bc5-ffc9-43cf-9df7-d41d29cce35b" data-action="Deal Block" data-label='48" S90F 4K OLED TV' data-dimension48='48" S90F 4K OLED TV' data-dimension25="$797">48" S90F 4K OLED TV: was $1397 now $797</a></strong><br>Thanks to an especially punchy picture for the price, Samsung's mid-range OLED is a fantastic pick for living rooms with above-average brightness. It's also jam-packed with features for streaming and gaming. I find the 48-inch model to have the most enticing sale price this Prime Day, but the 65-inch version is on sale for an attractive price, too.<br><strong>65-inch S90F: </strong><a href="https://www.amazon.com/Samsung-QN65S90FAFXZA-Smart-Dolby-Protection/dp/B0F6Q5B6XB/" target="_blank" rel="nofollow">$1,197 @ Amazon</a><a class="view-deal button" href="https://www.amazon.com/SAMSUNG-48-Inch-Processor-Upscaling-Xcelerator/dp/B0DXN1BFQ4" target="_blank" rel="nofollow" data-dimension112="e7843bc5-ffc9-43cf-9df7-d41d29cce35b" data-action="Deal Block" data-label="48" S90F 4K OLED TV" data-dimension48="48" S90F 4K OLED TV" data-dimension25="$797">View Deal</a></p></div><h2 id="3-lg-c5">3. LG C5</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="h9dbeJK2VNhVSDxuvbre2c" name="LG C5-06" alt="Spider-Man: Across the Spider-Verse on a LG C5 OLED TV" src="https://cdn.mos.cms.futurecdn.net/h9dbeJK2VNhVSDxuvbre2c-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The LG C5 comes from a long line of mid-range OLED TVs whose value is almost as impressive as its performance. In fact, it's the only TV on this list to have received our coveted, five-star rating.</p><p>You can read more about this stunning TV in our <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c5-oled-tv-review">LG C5 review</a>, but the basic gist is that it's practically overflowing with value, particularly once you factor in its Prime Day sale price. Unlike the previously mentioned Samsung S90F, the C5 is a mid-range OLED that includes Dolby Vision support.</p><p>The 42-inch C5 also arrives with just about every feature you could want for gaming and streaming. Its thorough selection of gaming features is a big reason why I keep recommending it to folks in the market for a 42-inch gaming monitor.</p><div class="product"><a data-dimension112="91a95552-d71b-415e-8524-23224acb7375" data-action="Deal Block" data-label="42" C5 4K OLED TV" data-dimension48="42" C5 4K OLED TV" data-dimension25="$899" href="https://www.amazon.com/LG-Upscaling-Filmmaker-Orchestra-OLED65C5PUA/dp/B0DYQMWSKG" 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:60.60%;"><img id="oBaPUFAAexE2Jge2Vinp73" name="91bxFjk187L._AC_SL1500_" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/oBaPUFAAexE2Jge2Vinp73-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="909" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/LG-Upscaling-Filmmaker-Orchestra-OLED65C5PUA/dp/B0DYQMWSKG" target="_blank" rel="nofollow" data-dimension112="91a95552-d71b-415e-8524-23224acb7375" data-action="Deal Block" data-label='42" C5 4K OLED TV' data-dimension48='42" C5 4K OLED TV' data-dimension25="$899">42" C5 4K OLED TV: was $999 now $899</a></strong><br>We showered this OLED TV with praise for its sensational performance, its class-leading selection of features and its approachable price point. The C5 delivers all of the benefits we've come to expect from OLED displays, but its design, software and overall experience is the result of LG's excellent engineering. This sale price involves the 42-inch C5, but the 65-inch version is on sale, as well.<br><strong>65-inch C5: </strong><a href="https://www.amazon.com/LG-Upscaling-Filmmaker-Orchestra-OLED65C5PUA/dp/B0DYQM4BDB/" target="_blank" rel="nofollow">$1,197 @ Amazon</a><a class="view-deal button" href="https://www.amazon.com/LG-Upscaling-Filmmaker-Orchestra-OLED65C5PUA/dp/B0DYQMWSKG" target="_blank" rel="nofollow" data-dimension112="91a95552-d71b-415e-8524-23224acb7375" data-action="Deal Block" data-label="42" C5 4K OLED TV" data-dimension48="42" C5 4K OLED TV" data-dimension25="$899">View Deal</a></p></div><h2 id="4-sony-xr8b">4. Sony XR8B</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="ayQPFVZHWwy4KNM5Zh3kRR" name="Sony-Bravia-XR8B-OLED-TV-lifestyle" alt="The Sony Bravia XR8B OLED TV displaying a colorful, abstract picture in a monochromatic living room" src="https://cdn.mos.cms.futurecdn.net/ayQPFVZHWwy4KNM5Zh3kRR-1920-80.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: Sony)</span></figcaption></figure><p>Among our readers, the Sony XR8B has been a popular pick during Prime Day, Cyber Monday and Black Friday sales events for a couple of years now. Don't let its prolonged shelf life stop you from considering it, though — it's one of the most affordable ways to enjoy a Sony-engineered OLED TV in 2026.</p><p>Sony's much-lauded picture processing is the highlight here. Other OLED TVs may get brighter and offer better color volume, but even Sony's most-affordable OLEDs are driven by some of the best picture processing capabilities the industry has to offer.</p><p>The XR8B comes with plenty of features, too, like support for 4K gaming at 120Hz and the Google TV smart platform.</p><div class="product"><a data-dimension112="0400fcff-3986-4d01-9536-b02c13b88192" data-action="Deal Block" data-label="55" XR8B 4K OLED TV" data-dimension48="55" XR8B 4K OLED TV" data-dimension25="$998" href="https://www.amazon.com/Sony-Exclusive-Features-PlayStation%C2%AE-K-55XR8B/dp/B0FHX84W4X/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="cATN8W9ThdVPgw7uRT8ine" name="Sony-Bravia-XR8B-OLED-TV-16-by-9-deal-block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/cATN8W9ThdVPgw7uRT8ine-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Sony-Exclusive-Features-PlayStation%C2%AE-K-55XR8B/dp/B0FHX84W4X/" target="_blank" rel="nofollow" data-dimension112="0400fcff-3986-4d01-9536-b02c13b88192" data-action="Deal Block" data-label='55" XR8B 4K OLED TV' data-dimension48='55" XR8B 4K OLED TV' data-dimension25="$998">55" XR8B 4K OLED TV: was $1499 now $998</a></strong><br>The 55-inch XR8B blends Sony's top-notch picture processing with an array of features for streaming and gaming. This includes built-in Google TV and support for 4K gaming at 120Hz.<a class="view-deal button" href="https://www.amazon.com/Sony-Exclusive-Features-PlayStation%C2%AE-K-55XR8B/dp/B0FHX84W4X/" target="_blank" rel="nofollow" data-dimension112="0400fcff-3986-4d01-9536-b02c13b88192" data-action="Deal Block" data-label="55" XR8B 4K OLED TV" data-dimension48="55" XR8B 4K OLED TV" data-dimension25="$998">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/tvs/live/best-prime-day-tv-deals-2026-live-sales-on-affordable-oleds-qleds-and-more">I've been reviewing TVs for over a decade — these are the 38 Prime Day TV deals worth your time</a></li><li><a href="https://www.tomsguide.com/gaming/nintendo/best-nintendo-switch-2-prime-day-deals-17-discounts-on-bundles-games-and-accessories-id-buy-with-my-own-money">I'm tracking all the Nintendo Switch 2 Prime Day deals — and these are the 17 discounts I'd actually get</a></li><li><a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review">Sony’s new Bravia 7 II has a secret weapon that makes it so good</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’m obsessed with my PS5 and these are the 9 Prime Day game deals I have my eye on, starting at just $9 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The <a href="https://www.tomsguide.com/gaming/playstation/ps5-slim">PS5 Slim</a> is one of the best gadgets I've ever bought with my own money, and I've spent thousands of hours playing my favorite games on it. I love revisiting the classics and I'm always looking to pick up a new one I've never heard of. But let's face it: PS5 games are expensive, and not everyone can afford to pay full price.</p><p>That's where <a href="https://www.tomsguide.com/live/news/prime-day-2026-handpicked-editor-recommended-deals-live">Amazon Prime Day sales</a> come in: they're a great time to grab a PS5 game without breaking the bank. As a lover of video games, I've handpicked my top deals. The cheapest game you can buy right now is <a href="https://www.amazon.com/Lost-Soul-Aside-PS5-PlayStation-5/dp/B0DX42RXMS/" target="_blank" rel="nofollow">Lost Soul Aside which is down to just $9 from $59 </a>— how could you say no to that? For football fans, <a href="https://www.amazon.com/EA-Sports-FC-26-Standard/dp/B0FH132GYR/" target="_blank" rel="nofollow">EA Sports FC 26 is 64% off right now</a>, and it's the ideal game to play during halftime as the FIFA World Cup gathers steam.</p><p>My top choice, though? Why, <a href="https://www.amazon.com/Clair-Obscur-Expedition-PS5-PlayStation-5/dp/B0DK6225DY/" target="_blank" rel="nofollow">Clair Obscur: Expedition 33, of course, which is available for $39 right now</a> ($33 would have been more fitting, but oh well). It's a game I can't shut up about and one I recommend every gamer needs to play at least once.</p><p>That's not all. There are plenty more deals to be taken advantage of, on sports, horror, RPG, and action-adventure games. So, without further ado, here are my top Prime Day PS5 game deals (so far).</p><h2 id="meet-your-ps5-games-deals-expert">Meet your PS5 games deals expert</h2><h2 class="article-body__section" id="section-quick-links"><span>Quick links</span></h2><ul><li><strong></strong><a href="https://www.amazon.com/primeday" target="_blank" rel="nofollow"><strong>Shop all Amazon Prime Day deals now</strong></a><strong></strong></li><li><strong>Lost Soul Aside: </strong><a href="https://www.amazon.com/Lost-Soul-Aside-PS5-PlayStation-5/dp/B0DX42RXMS/" target="_blank" rel="nofollow"><strong>was $59 now $9</strong></a><strong></strong></li><li><strong>EA Sports FC 26: </strong><a href="https://www.amazon.com/EA-SPORTS-FC-26-PlayStation-5/dp/B0FH132GYR/" target="_blank" rel="nofollow"><strong>was $69 now $24</strong></a><strong></strong></li><li><strong>Elden Ring: </strong><a href="https://www.amazon.com/Elden-Ring-PlayStation-5/dp/B09743F8P6/" target="_blank" rel="nofollow"><strong>was $49 now $19</strong></a><strong></strong></li><li><strong>Metal Gear Solid Δ Snake Eater: </strong><a href="https://www.amazon.com/METAL-GEAR-SOLID-TACTICAL-PlayStation-5/dp/B0D4RJGFDT/" target="_blank" rel="nofollow"><strong>was $49 now $24</strong></a><strong></strong></li><li><strong>Silent Hill 2: </strong><a href="https://www.amazon.com/Silent-Hill-2-PS5-PlayStation-5/dp/B0CMC3C3Z9/" target="_blank" rel="nofollow"><strong>was $39 now $19</strong></a><strong></strong></li><li><strong>Assassin's Creed Shadows: </strong><a href="https://www.amazon.com/Assassins-Creed-Shadows-Limited-Exclusive-PlayStation/dp/B0D47WTKPM/" target="_blank" rel="nofollow"><strong>was $50 now $29</strong></a><strong></strong></li><li><strong>Star Wars Outlaws: </strong><a href="https://www.amazon.com/Star-Wars-Outlaws-Limited-Exclusive-PlayStation/dp/B0D187PJHJ/" target="_blank" rel="nofollow"><strong>was $19 now $14</strong></a><strong></strong></li><li><strong>Clair Obscur: Expedition 33: </strong><a href="https://www.amazon.com/Clair-Obscur-Expedition-PS5-PlayStation-5/dp/B0DK6225DY/" target="_blank" rel="nofollow"><strong>was $49 now $39</strong></a><strong></strong></li><li><strong>Resident Evil Requiem:</strong><a href="https://www.amazon.com/Resident-Evil-Requiem-Amazon-Exclusive-PlayStation/dp/B0FY929QN1/" target="_blank" rel="nofollow"><strong> was $69 now $55</strong></a><strong></strong></li></ul><h2 class="article-body__section" id="section-best-prime-day-ps5-games-deals"><span>Best Prime Day PS5 games deals</span></h2><div class="product"><a data-dimension112="85bab94a-42e3-417d-9a12-ff94ec7132ec" data-action="Deal Block" data-label="Soul Aside" data-dimension48="Soul Aside" data-dimension25="$9" href="https://www.amazon.com/Lost-Soul-Aside-PS5-PlayStation-5/dp/B0DX42RXMS/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1204px;"><p class="vanilla-image-block" style="padding-top:124.58%;"><img id="YSgsNTgMVfmyRH9tioe37X" name="Lost Soul Aside deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/YSgsNTgMVfmyRH9tioe37X-1920-80.jpg" mos="" align="middle" fullscreen="" width="1204" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Lost Soul Aside is a stylish action-RPG which follows the story of Kaser, a warrior entangled in a rebel group fighting a tyrannical empire. When a mysterious invasion called the Voidrax steals his sister's soul, he bonds with a dragon-like entity named Arena to save her and liberate humanity.<a class="view-deal button" href="https://www.amazon.com/Lost-Soul-Aside-PS5-PlayStation-5/dp/B0DX42RXMS/" target="_blank" rel="nofollow" data-dimension112="85bab94a-42e3-417d-9a12-ff94ec7132ec" data-action="Deal Block" data-label="Soul Aside" data-dimension48="Soul Aside" data-dimension25="$9">View Deal</a></p></div><div class="product"><a data-dimension112="19325f9d-722b-42ee-9c72-d840914df326" data-action="Deal Block" data-label="FC 26" data-dimension48="FC 26" data-dimension25="$24" href="https://www.amazon.com/EA-SPORTS-FC-26-PlayStation-5/dp/B0FH132GYR/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1232px;"><p class="vanilla-image-block" style="padding-top:121.75%;"><img id="8NnQFqwYorjYS4SR66BWG8" name="EAFC26 deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/8NnQFqwYorjYS4SR66BWG8-1920-80.jpg" mos="" align="middle" fullscreen="" width="1232" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>What better time to pick up <a href="https://www.tomsguide.com/gaming/ea-sports-fc-26-review" data-dimension112="19325f9d-722b-42ee-9c72-d840914df326" data-action="Deal Block" data-label="FC 26" data-dimension48="FC 26" data-dimension25="$24">EA Sports FC 26 </a>than during the FIFA World Cup, am I right? The latest installment in EA's popular soccer simulator  comes feature-complete with Ultimate Team, Career Mode and more. FC 26 also includes licenses for all the biggest soccer leagues around, including the Premier League.<a class="view-deal button" href="https://www.amazon.com/EA-SPORTS-FC-26-PlayStation-5/dp/B0FH132GYR/" target="_blank" rel="nofollow" data-dimension112="19325f9d-722b-42ee-9c72-d840914df326" data-action="Deal Block" data-label="FC 26" data-dimension48="FC 26" data-dimension25="$24">View Deal</a></p></div><div class="product"><a data-dimension112="eddb8630-2dc2-4d70-aea6-f625a6be436a" data-action="Deal Block" data-label="Ring" data-dimension48="Ring" data-dimension25="$19" href="https://www.amazon.com/Elden-Ring-PlayStation-5/dp/B09743F8P6/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="MbKNJXNrxvLH7LepfJuUAm" name="elden ring deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/MbKNJXNrxvLH7LepfJuUAm-1920-80.png" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><a href="https://www.tomsguide.com/reviews/elden-ring" data-dimension112="eddb8630-2dc2-4d70-aea6-f625a6be436a" data-action="Deal Block" data-label="Ring" data-dimension48="Ring" data-dimension25="$19">Elden Ring</a> is an open-world wonder, offering rewarding gameplay, a cohesive setting and gripping lore. Some of the open-world systems feel clunkier than they should, though, but that isn't a dealbreaker, especially at the low, low price of just $19.<a class="view-deal button" href="https://www.amazon.com/Elden-Ring-PlayStation-5/dp/B09743F8P6/" target="_blank" rel="nofollow" data-dimension112="eddb8630-2dc2-4d70-aea6-f625a6be436a" data-action="Deal Block" data-label="Ring" data-dimension48="Ring" data-dimension25="$19">View Deal</a></p></div><div class="product"><a data-dimension112="1c9c82d5-0a56-4a94-a831-07f481bcbec1" data-action="Deal Block" data-label="Δ: Snake Eater" data-dimension48="Δ: Snake Eater" data-dimension25="$24" href="https://www.amazon.com/METAL-GEAR-SOLID-TACTICAL-PlayStation-5/dp/B0D4RJGFDT/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1180px;"><p class="vanilla-image-block" style="padding-top:127.12%;"><img id="mXH3uuroBNsvrD4J4kvbA8" name="metal-gear-solid-delta-ps5-box" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/mXH3uuroBNsvrD4J4kvbA8-1920-80.jpg" mos="" align="middle" fullscreen="" width="1180" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/METAL-GEAR-SOLID-TACTICAL-PlayStation-5/dp/B0D4RJGFDT/" target="_blank" rel="nofollow" data-dimension112="1c9c82d5-0a56-4a94-a831-07f481bcbec1" data-action="Deal Block" data-label="Δ: Snake Eater" data-dimension48="Δ: Snake Eater" data-dimension25="$24">Δ: Snake Eater: was $49 now $24</a></strong><br><a href="https://www.tomsguide.com/gaming/metal-gear-solid-delta-snake-eater-review">Metal Gear Solid Delta: Snake Eater</a> is a masterful remake of this beloved classic. The updated graphics and modernized controls make it viable for modern gamers, but you still get the same basic experience of the original. Right now, this is the definitive way to experience Metal Gear Solid 3.<a class="view-deal button" href="https://www.amazon.com/METAL-GEAR-SOLID-TACTICAL-PlayStation-5/dp/B0D4RJGFDT/" target="_blank" rel="nofollow" data-dimension112="1c9c82d5-0a56-4a94-a831-07f481bcbec1" data-action="Deal Block" data-label="Δ: Snake Eater" data-dimension48="Δ: Snake Eater" data-dimension25="$24">View Deal</a></p></div><div class="product"><a data-dimension112="dbcef586-8c4c-49c0-852b-b143dc9219a5" data-action="Deal Block" data-label="2" data-dimension48="2" data-dimension25="$19" href="https://www.amazon.com/Silent-Hill-2-PS5-PlayStation-5/dp/B0CMC3C3Z9/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1137px;"><p class="vanilla-image-block" style="padding-top:131.93%;"><img id="7qqzgFNziQmswnQyqqRBEm" name="71E3B-85r1L._SL1500_" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/7qqzgFNziQmswnQyqqRBEm-1920-80.jpg" mos="" align="middle" fullscreen="" width="1137" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>A loving remake of a true horror great, <a href="https://www.tomsguide.com/gaming/silent-hill-2-review" data-dimension112="dbcef586-8c4c-49c0-852b-b143dc9219a5" data-action="Deal Block" data-label="2" data-dimension48="2" data-dimension25="$19">Silent Hill 2</a> is creepy, a little too bloated and confusing, but hot dang does it feel good to shotgun monsters that look too X-rated for the “Terrifier” movies. It's a brutal, beautiful survival horror remake, and for just $19, it's a steal right now.<a class="view-deal button" href="https://www.amazon.com/Silent-Hill-2-PS5-PlayStation-5/dp/B0CMC3C3Z9/" target="_blank" rel="nofollow" data-dimension112="dbcef586-8c4c-49c0-852b-b143dc9219a5" data-action="Deal Block" data-label="2" data-dimension48="2" data-dimension25="$19">View Deal</a></p></div><div class="product"><a data-dimension112="091075f6-9225-457d-8383-919e7ce538b8" data-action="Deal Block" data-label="Shadows" data-dimension48="Shadows" data-dimension25="$29" href="https://www.amazon.com/Assassins-Creed-Shadows-Limited-Exclusive-PlayStation/dp/B0D47WTKPM/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1158px;"><p class="vanilla-image-block" style="padding-top:129.53%;"><img id="tEFopvifi8tQ9BfBHB9rUF" name="81yVcGESG9L._SL1500_" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/tEFopvifi8tQ9BfBHB9rUF-1920-80.jpg" mos="" align="middle" fullscreen="" width="1158" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><a href="https://www.tomsguide.com/gaming/assassins-creed-shadows-review" data-dimension112="091075f6-9225-457d-8383-919e7ce538b8" data-action="Deal Block" data-label="Shadows" data-dimension48="Shadows" data-dimension25="$29">Assassin’s Creed Shadows</a> delivers on its promise of bringing back classic stealth mechanics while introducing new ones and combining the best of the older and RPG titles. The detailed open-world of feudal Japan feels full of life with compelling playable and non-playable characters, and different fighting tactics enrich the combat.<a class="view-deal button" href="https://www.amazon.com/Assassins-Creed-Shadows-Limited-Exclusive-PlayStation/dp/B0D47WTKPM/" target="_blank" rel="nofollow" data-dimension112="091075f6-9225-457d-8383-919e7ce538b8" data-action="Deal Block" data-label="Shadows" data-dimension48="Shadows" data-dimension25="$29">View Deal</a></p></div><div class="product"><a data-dimension112="342f55ea-b223-4dec-a73a-0cb4fdb2650b" data-action="Deal Block" data-label="Outlaws" data-dimension48="Outlaws" data-dimension25="$14" href="https://www.amazon.com/Star-Wars-Outlaws-Limited-Exclusive-PlayStation/dp/B0D187PJHJ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1172px;"><p class="vanilla-image-block" style="padding-top:127.99%;"><img id="7orWhqyGvCd2P6VXQEnLMm" name="star-wars-outlaws-ps5-na-box" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/7orWhqyGvCd2P6VXQEnLMm-1920-80.jpg" mos="" align="middle" fullscreen="" width="1172" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><a href="https://www.tomsguide.com/gaming/star-wars-outlaws-review-a-quintessential-outing-in-a-galaxy-far-far-away" data-dimension112="342f55ea-b223-4dec-a73a-0cb4fdb2650b" data-action="Deal Block" data-label="Outlaws" data-dimension48="Outlaws" data-dimension25="$14">Star Wars Outlaws</a> delivers a true Star Wars experience, featuring four total worlds teeming with expansive maps and various side quests for hours of unending fun. Space-based combat and general gunplay are solid with rare moments of stealth that can frustrate at times. If you didn't fancy it at launch, you might do now when it's available for $14.<a class="view-deal button" href="https://www.amazon.com/Star-Wars-Outlaws-Limited-Exclusive-PlayStation/dp/B0D187PJHJ/" target="_blank" rel="nofollow" data-dimension112="342f55ea-b223-4dec-a73a-0cb4fdb2650b" data-action="Deal Block" data-label="Outlaws" data-dimension48="Outlaws" data-dimension25="$14">View Deal</a></p></div><div class="product"><a data-dimension112="466a9e0c-a7df-42a1-a1cb-78d6638f73ae" data-action="Deal Block" data-label="Expedition 33" data-dimension48="Expedition 33" data-dimension25="$39" href="https://www.amazon.com/Clair-Obscur-Expedition-PS5-PlayStation-5/dp/B0DK6225DY/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:522px;"><p class="vanilla-image-block" style="padding-top:115.52%;"><img id="rT7AZpcNQnSsRCYbodpyCo" name="Clair Obscur Expedition 33" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/rT7AZpcNQnSsRCYbodpyCo-1920-80.jpg" mos="" align="middle" fullscreen="" width="522" height="603" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><a href="https://www.tomsguide.com/gaming/clair-obscur-expedition-33-review" data-dimension112="466a9e0c-a7df-42a1-a1cb-78d6638f73ae" data-action="Deal Block" data-label="Expedition 33" data-dimension48="Expedition 33" data-dimension25="$39">Clair Obscur: Expedition 33</a> is an addition to the turn-based RPG genre, one that boasts challenging, engaging combat mechanics and plays out in a distinct and utterly beautiful fantasy world. From start to finish, we were fascinated by the sights, sounds and tasks that lay ahead. If you love RPGs, it should be at the top of your list; we really can’t recommend it enough.<a class="view-deal button" href="https://www.amazon.com/Clair-Obscur-Expedition-PS5-PlayStation-5/dp/B0DK6225DY/" target="_blank" rel="nofollow" data-dimension112="466a9e0c-a7df-42a1-a1cb-78d6638f73ae" data-action="Deal Block" data-label="Expedition 33" data-dimension48="Expedition 33" data-dimension25="$39">View Deal</a></p></div><div class="product"><a data-dimension112="b4cb35ea-3bb2-41d9-a6d1-f2cda4ac354d" data-action="Deal Block" data-label="Requiem" data-dimension48="Requiem" data-dimension25="$55" href="https://www.amazon.com/Resident-Evil-Requiem-Amazon-Exclusive-PlayStation/dp/B0FY929QN1/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1298px;"><p class="vanilla-image-block" style="padding-top:115.56%;"><img id="t6iarbisuGMy5SzXoHtF9Q" name="resident-evil-requiem-ps5-na-box" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/t6iarbisuGMy5SzXoHtF9Q-1920-80.jpg" mos="" align="middle" fullscreen="" width="1298" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><a href="https://www.tomsguide.com/gaming/resident-evil-requiem-review" data-dimension112="b4cb35ea-3bb2-41d9-a6d1-f2cda4ac354d" data-action="Deal Block" data-label="Requiem" data-dimension48="Requiem" data-dimension25="$55">Resident Evil Requiem</a> combines elements from prior installments to deliver one of the strongest entries yet. Though it doesn’t take risks, its thrilling gameplay and stunning graphics keep you engaged from beginning to end.<a class="view-deal button" href="https://www.amazon.com/Resident-Evil-Requiem-Amazon-Exclusive-PlayStation/dp/B0FY929QN1/" target="_blank" rel="nofollow" data-dimension112="b4cb35ea-3bb2-41d9-a6d1-f2cda4ac354d" data-action="Deal Block" data-label="Requiem" data-dimension48="Requiem" data-dimension25="$55">View Deal</a></p></div><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-ps5-deals"><span>We're tracking all the best Prime Day PS5 deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=video+games&min_discount_ratio=0.95&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function 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 { 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 (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';          }        }        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 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');                    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, 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.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> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/gaming/playstation/amazon-prime-day-2026-ps5-games-deals</link>
                                                                            <description>
                            <![CDATA[ I love playing video games on my PS5, and this Amazon Prime Day, I've found the best deals, whether you're looking to revisit a favorite or pick up a new one. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">4GUniVM5GneRxYYudVcWfd</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/znydGj3sLSUiWpVbHxdgPh-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 23 Jun 2026 18:47:24 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Playstation]]></category>
                                                    <category><![CDATA[Gaming]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3-320-70.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres.&lt;/p&gt;&lt;p&gt;A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&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/znydGj3sLSUiWpVbHxdgPh-1920-80.jpg">
                                                            <media:credit><![CDATA[EA / Ubisoft / Capcom / Sandfall Interactive / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of PS5 games]]></media:description>                                                            <media:text><![CDATA[A selection of PS5 games]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of PS5 games]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/znydGj3sLSUiWpVbHxdgPh-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The <a href="https://www.tomsguide.com/gaming/playstation/ps5-slim">PS5 Slim</a> is one of the best gadgets I've ever bought with my own money, and I've spent thousands of hours playing my favorite games on it. I love revisiting the classics and I'm always looking to pick up a new one I've never heard of. But let's face it: PS5 games are expensive, and not everyone can afford to pay full price.</p><p>That's where <a href="https://www.tomsguide.com/live/news/prime-day-2026-handpicked-editor-recommended-deals-live">Amazon Prime Day sales</a> come in: they're a great time to grab a PS5 game without breaking the bank. As a lover of video games, I've handpicked my top deals. The cheapest game you can buy right now is <a href="https://www.amazon.com/Lost-Soul-Aside-PS5-PlayStation-5/dp/B0DX42RXMS/" target="_blank" rel="nofollow">Lost Soul Aside which is down to just $9 from $59 </a>— how could you say no to that? For football fans, <a href="https://www.amazon.com/EA-Sports-FC-26-Standard/dp/B0FH132GYR/" target="_blank" rel="nofollow">EA Sports FC 26 is 64% off right now</a>, and it's the ideal game to play during halftime as the FIFA World Cup gathers steam.</p><p>My top choice, though? Why, <a href="https://www.amazon.com/Clair-Obscur-Expedition-PS5-PlayStation-5/dp/B0DK6225DY/" target="_blank" rel="nofollow">Clair Obscur: Expedition 33, of course, which is available for $39 right now</a> ($33 would have been more fitting, but oh well). It's a game I can't shut up about and one I recommend every gamer needs to play at least once.</p><p>That's not all. There are plenty more deals to be taken advantage of, on sports, horror, RPG, and action-adventure games. So, without further ado, here are my top Prime Day PS5 game deals (so far).</p><h2 id="meet-your-ps5-games-deals-expert">Meet your PS5 games deals expert</h2><h2 class="article-body__section" id="section-quick-links"><span>Quick links</span></h2><ul><li><strong></strong><a href="https://www.amazon.com/primeday" target="_blank" rel="nofollow"><strong>Shop all Amazon Prime Day deals now</strong></a><strong></strong></li><li><strong>Lost Soul Aside: </strong><a href="https://www.amazon.com/Lost-Soul-Aside-PS5-PlayStation-5/dp/B0DX42RXMS/" target="_blank" rel="nofollow"><strong>was $59 now $9</strong></a><strong></strong></li><li><strong>EA Sports FC 26: </strong><a href="https://www.amazon.com/EA-SPORTS-FC-26-PlayStation-5/dp/B0FH132GYR/" target="_blank" rel="nofollow"><strong>was $69 now $24</strong></a><strong></strong></li><li><strong>Elden Ring: </strong><a href="https://www.amazon.com/Elden-Ring-PlayStation-5/dp/B09743F8P6/" target="_blank" rel="nofollow"><strong>was $49 now $19</strong></a><strong></strong></li><li><strong>Metal Gear Solid Δ Snake Eater: </strong><a href="https://www.amazon.com/METAL-GEAR-SOLID-TACTICAL-PlayStation-5/dp/B0D4RJGFDT/" target="_blank" rel="nofollow"><strong>was $49 now $24</strong></a><strong></strong></li><li><strong>Silent Hill 2: </strong><a href="https://www.amazon.com/Silent-Hill-2-PS5-PlayStation-5/dp/B0CMC3C3Z9/" target="_blank" rel="nofollow"><strong>was $39 now $19</strong></a><strong></strong></li><li><strong>Assassin's Creed Shadows: </strong><a href="https://www.amazon.com/Assassins-Creed-Shadows-Limited-Exclusive-PlayStation/dp/B0D47WTKPM/" target="_blank" rel="nofollow"><strong>was $50 now $29</strong></a><strong></strong></li><li><strong>Star Wars Outlaws: </strong><a href="https://www.amazon.com/Star-Wars-Outlaws-Limited-Exclusive-PlayStation/dp/B0D187PJHJ/" target="_blank" rel="nofollow"><strong>was $19 now $14</strong></a><strong></strong></li><li><strong>Clair Obscur: Expedition 33: </strong><a href="https://www.amazon.com/Clair-Obscur-Expedition-PS5-PlayStation-5/dp/B0DK6225DY/" target="_blank" rel="nofollow"><strong>was $49 now $39</strong></a><strong></strong></li><li><strong>Resident Evil Requiem:</strong><a href="https://www.amazon.com/Resident-Evil-Requiem-Amazon-Exclusive-PlayStation/dp/B0FY929QN1/" target="_blank" rel="nofollow"><strong> was $69 now $55</strong></a><strong></strong></li></ul><h2 class="article-body__section" id="section-best-prime-day-ps5-games-deals"><span>Best Prime Day PS5 games deals</span></h2><div class="product"><a data-dimension112="85bab94a-42e3-417d-9a12-ff94ec7132ec" data-action="Deal Block" data-label="Soul Aside" data-dimension48="Soul Aside" data-dimension25="$9" href="https://www.amazon.com/Lost-Soul-Aside-PS5-PlayStation-5/dp/B0DX42RXMS/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1204px;"><p class="vanilla-image-block" style="padding-top:124.58%;"><img id="YSgsNTgMVfmyRH9tioe37X" name="Lost Soul Aside deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/YSgsNTgMVfmyRH9tioe37X-1920-80.jpg" mos="" align="middle" fullscreen="" width="1204" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Lost Soul Aside is a stylish action-RPG which follows the story of Kaser, a warrior entangled in a rebel group fighting a tyrannical empire. When a mysterious invasion called the Voidrax steals his sister's soul, he bonds with a dragon-like entity named Arena to save her and liberate humanity.<a class="view-deal button" href="https://www.amazon.com/Lost-Soul-Aside-PS5-PlayStation-5/dp/B0DX42RXMS/" target="_blank" rel="nofollow" data-dimension112="85bab94a-42e3-417d-9a12-ff94ec7132ec" data-action="Deal Block" data-label="Soul Aside" data-dimension48="Soul Aside" data-dimension25="$9">View Deal</a></p></div><div class="product"><a data-dimension112="19325f9d-722b-42ee-9c72-d840914df326" data-action="Deal Block" data-label="FC 26" data-dimension48="FC 26" data-dimension25="$24" href="https://www.amazon.com/EA-SPORTS-FC-26-PlayStation-5/dp/B0FH132GYR/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1232px;"><p class="vanilla-image-block" style="padding-top:121.75%;"><img id="8NnQFqwYorjYS4SR66BWG8" name="EAFC26 deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/8NnQFqwYorjYS4SR66BWG8-1920-80.jpg" mos="" align="middle" fullscreen="" width="1232" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>What better time to pick up <a href="https://www.tomsguide.com/gaming/ea-sports-fc-26-review" data-dimension112="19325f9d-722b-42ee-9c72-d840914df326" data-action="Deal Block" data-label="FC 26" data-dimension48="FC 26" data-dimension25="$24">EA Sports FC 26 </a>than during the FIFA World Cup, am I right? The latest installment in EA's popular soccer simulator  comes feature-complete with Ultimate Team, Career Mode and more. FC 26 also includes licenses for all the biggest soccer leagues around, including the Premier League.<a class="view-deal button" href="https://www.amazon.com/EA-SPORTS-FC-26-PlayStation-5/dp/B0FH132GYR/" target="_blank" rel="nofollow" data-dimension112="19325f9d-722b-42ee-9c72-d840914df326" data-action="Deal Block" data-label="FC 26" data-dimension48="FC 26" data-dimension25="$24">View Deal</a></p></div><div class="product"><a data-dimension112="eddb8630-2dc2-4d70-aea6-f625a6be436a" data-action="Deal Block" data-label="Ring" data-dimension48="Ring" data-dimension25="$19" href="https://www.amazon.com/Elden-Ring-PlayStation-5/dp/B09743F8P6/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="MbKNJXNrxvLH7LepfJuUAm" name="elden ring deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/MbKNJXNrxvLH7LepfJuUAm-1920-80.png" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><a href="https://www.tomsguide.com/reviews/elden-ring" data-dimension112="eddb8630-2dc2-4d70-aea6-f625a6be436a" data-action="Deal Block" data-label="Ring" data-dimension48="Ring" data-dimension25="$19">Elden Ring</a> is an open-world wonder, offering rewarding gameplay, a cohesive setting and gripping lore. Some of the open-world systems feel clunkier than they should, though, but that isn't a dealbreaker, especially at the low, low price of just $19.<a class="view-deal button" href="https://www.amazon.com/Elden-Ring-PlayStation-5/dp/B09743F8P6/" target="_blank" rel="nofollow" data-dimension112="eddb8630-2dc2-4d70-aea6-f625a6be436a" data-action="Deal Block" data-label="Ring" data-dimension48="Ring" data-dimension25="$19">View Deal</a></p></div><div class="product"><a data-dimension112="1c9c82d5-0a56-4a94-a831-07f481bcbec1" data-action="Deal Block" data-label="Δ: Snake Eater" data-dimension48="Δ: Snake Eater" data-dimension25="$24" href="https://www.amazon.com/METAL-GEAR-SOLID-TACTICAL-PlayStation-5/dp/B0D4RJGFDT/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1180px;"><p class="vanilla-image-block" style="padding-top:127.12%;"><img id="mXH3uuroBNsvrD4J4kvbA8" name="metal-gear-solid-delta-ps5-box" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/mXH3uuroBNsvrD4J4kvbA8-1920-80.jpg" mos="" align="middle" fullscreen="" width="1180" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/METAL-GEAR-SOLID-TACTICAL-PlayStation-5/dp/B0D4RJGFDT/" target="_blank" rel="nofollow" data-dimension112="1c9c82d5-0a56-4a94-a831-07f481bcbec1" data-action="Deal Block" data-label="Δ: Snake Eater" data-dimension48="Δ: Snake Eater" data-dimension25="$24">Δ: Snake Eater: was $49 now $24</a></strong><br><a href="https://www.tomsguide.com/gaming/metal-gear-solid-delta-snake-eater-review">Metal Gear Solid Delta: Snake Eater</a> is a masterful remake of this beloved classic. The updated graphics and modernized controls make it viable for modern gamers, but you still get the same basic experience of the original. Right now, this is the definitive way to experience Metal Gear Solid 3.<a class="view-deal button" href="https://www.amazon.com/METAL-GEAR-SOLID-TACTICAL-PlayStation-5/dp/B0D4RJGFDT/" target="_blank" rel="nofollow" data-dimension112="1c9c82d5-0a56-4a94-a831-07f481bcbec1" data-action="Deal Block" data-label="Δ: Snake Eater" data-dimension48="Δ: Snake Eater" data-dimension25="$24">View Deal</a></p></div><div class="product"><a data-dimension112="dbcef586-8c4c-49c0-852b-b143dc9219a5" data-action="Deal Block" data-label="2" data-dimension48="2" data-dimension25="$19" href="https://www.amazon.com/Silent-Hill-2-PS5-PlayStation-5/dp/B0CMC3C3Z9/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1137px;"><p class="vanilla-image-block" style="padding-top:131.93%;"><img id="7qqzgFNziQmswnQyqqRBEm" name="71E3B-85r1L._SL1500_" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/7qqzgFNziQmswnQyqqRBEm-1920-80.jpg" mos="" align="middle" fullscreen="" width="1137" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>A loving remake of a true horror great, <a href="https://www.tomsguide.com/gaming/silent-hill-2-review" data-dimension112="dbcef586-8c4c-49c0-852b-b143dc9219a5" data-action="Deal Block" data-label="2" data-dimension48="2" data-dimension25="$19">Silent Hill 2</a> is creepy, a little too bloated and confusing, but hot dang does it feel good to shotgun monsters that look too X-rated for the “Terrifier” movies. It's a brutal, beautiful survival horror remake, and for just $19, it's a steal right now.<a class="view-deal button" href="https://www.amazon.com/Silent-Hill-2-PS5-PlayStation-5/dp/B0CMC3C3Z9/" target="_blank" rel="nofollow" data-dimension112="dbcef586-8c4c-49c0-852b-b143dc9219a5" data-action="Deal Block" data-label="2" data-dimension48="2" data-dimension25="$19">View Deal</a></p></div><div class="product"><a data-dimension112="091075f6-9225-457d-8383-919e7ce538b8" data-action="Deal Block" data-label="Shadows" data-dimension48="Shadows" data-dimension25="$29" href="https://www.amazon.com/Assassins-Creed-Shadows-Limited-Exclusive-PlayStation/dp/B0D47WTKPM/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1158px;"><p class="vanilla-image-block" style="padding-top:129.53%;"><img id="tEFopvifi8tQ9BfBHB9rUF" name="81yVcGESG9L._SL1500_" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/tEFopvifi8tQ9BfBHB9rUF-1920-80.jpg" mos="" align="middle" fullscreen="" width="1158" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><a href="https://www.tomsguide.com/gaming/assassins-creed-shadows-review" data-dimension112="091075f6-9225-457d-8383-919e7ce538b8" data-action="Deal Block" data-label="Shadows" data-dimension48="Shadows" data-dimension25="$29">Assassin’s Creed Shadows</a> delivers on its promise of bringing back classic stealth mechanics while introducing new ones and combining the best of the older and RPG titles. The detailed open-world of feudal Japan feels full of life with compelling playable and non-playable characters, and different fighting tactics enrich the combat.<a class="view-deal button" href="https://www.amazon.com/Assassins-Creed-Shadows-Limited-Exclusive-PlayStation/dp/B0D47WTKPM/" target="_blank" rel="nofollow" data-dimension112="091075f6-9225-457d-8383-919e7ce538b8" data-action="Deal Block" data-label="Shadows" data-dimension48="Shadows" data-dimension25="$29">View Deal</a></p></div><div class="product"><a data-dimension112="342f55ea-b223-4dec-a73a-0cb4fdb2650b" data-action="Deal Block" data-label="Outlaws" data-dimension48="Outlaws" data-dimension25="$14" href="https://www.amazon.com/Star-Wars-Outlaws-Limited-Exclusive-PlayStation/dp/B0D187PJHJ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1172px;"><p class="vanilla-image-block" style="padding-top:127.99%;"><img id="7orWhqyGvCd2P6VXQEnLMm" name="star-wars-outlaws-ps5-na-box" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/7orWhqyGvCd2P6VXQEnLMm-1920-80.jpg" mos="" align="middle" fullscreen="" width="1172" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><a href="https://www.tomsguide.com/gaming/star-wars-outlaws-review-a-quintessential-outing-in-a-galaxy-far-far-away" data-dimension112="342f55ea-b223-4dec-a73a-0cb4fdb2650b" data-action="Deal Block" data-label="Outlaws" data-dimension48="Outlaws" data-dimension25="$14">Star Wars Outlaws</a> delivers a true Star Wars experience, featuring four total worlds teeming with expansive maps and various side quests for hours of unending fun. Space-based combat and general gunplay are solid with rare moments of stealth that can frustrate at times. If you didn't fancy it at launch, you might do now when it's available for $14.<a class="view-deal button" href="https://www.amazon.com/Star-Wars-Outlaws-Limited-Exclusive-PlayStation/dp/B0D187PJHJ/" target="_blank" rel="nofollow" data-dimension112="342f55ea-b223-4dec-a73a-0cb4fdb2650b" data-action="Deal Block" data-label="Outlaws" data-dimension48="Outlaws" data-dimension25="$14">View Deal</a></p></div><div class="product"><a data-dimension112="466a9e0c-a7df-42a1-a1cb-78d6638f73ae" data-action="Deal Block" data-label="Expedition 33" data-dimension48="Expedition 33" data-dimension25="$39" href="https://www.amazon.com/Clair-Obscur-Expedition-PS5-PlayStation-5/dp/B0DK6225DY/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:522px;"><p class="vanilla-image-block" style="padding-top:115.52%;"><img id="rT7AZpcNQnSsRCYbodpyCo" name="Clair Obscur Expedition 33" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/rT7AZpcNQnSsRCYbodpyCo-1920-80.jpg" mos="" align="middle" fullscreen="" width="522" height="603" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><a href="https://www.tomsguide.com/gaming/clair-obscur-expedition-33-review" data-dimension112="466a9e0c-a7df-42a1-a1cb-78d6638f73ae" data-action="Deal Block" data-label="Expedition 33" data-dimension48="Expedition 33" data-dimension25="$39">Clair Obscur: Expedition 33</a> is an addition to the turn-based RPG genre, one that boasts challenging, engaging combat mechanics and plays out in a distinct and utterly beautiful fantasy world. From start to finish, we were fascinated by the sights, sounds and tasks that lay ahead. If you love RPGs, it should be at the top of your list; we really can’t recommend it enough.<a class="view-deal button" href="https://www.amazon.com/Clair-Obscur-Expedition-PS5-PlayStation-5/dp/B0DK6225DY/" target="_blank" rel="nofollow" data-dimension112="466a9e0c-a7df-42a1-a1cb-78d6638f73ae" data-action="Deal Block" data-label="Expedition 33" data-dimension48="Expedition 33" data-dimension25="$39">View Deal</a></p></div><div class="product"><a data-dimension112="b4cb35ea-3bb2-41d9-a6d1-f2cda4ac354d" data-action="Deal Block" data-label="Requiem" data-dimension48="Requiem" data-dimension25="$55" href="https://www.amazon.com/Resident-Evil-Requiem-Amazon-Exclusive-PlayStation/dp/B0FY929QN1/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1298px;"><p class="vanilla-image-block" style="padding-top:115.56%;"><img id="t6iarbisuGMy5SzXoHtF9Q" name="resident-evil-requiem-ps5-na-box" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/t6iarbisuGMy5SzXoHtF9Q-1920-80.jpg" mos="" align="middle" fullscreen="" width="1298" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><a href="https://www.tomsguide.com/gaming/resident-evil-requiem-review" data-dimension112="b4cb35ea-3bb2-41d9-a6d1-f2cda4ac354d" data-action="Deal Block" data-label="Requiem" data-dimension48="Requiem" data-dimension25="$55">Resident Evil Requiem</a> combines elements from prior installments to deliver one of the strongest entries yet. Though it doesn’t take risks, its thrilling gameplay and stunning graphics keep you engaged from beginning to end.<a class="view-deal button" href="https://www.amazon.com/Resident-Evil-Requiem-Amazon-Exclusive-PlayStation/dp/B0FY929QN1/" target="_blank" rel="nofollow" data-dimension112="b4cb35ea-3bb2-41d9-a6d1-f2cda4ac354d" data-action="Deal Block" data-label="Requiem" data-dimension48="Requiem" data-dimension25="$55">View Deal</a></p></div><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-ps5-deals"><span>We're tracking all the best Prime Day PS5 deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=video+games&min_discount_ratio=0.95&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function 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 { 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 (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';          }        }        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 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');                    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, 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.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[ Who needs A/C anyway? 7 heatwave essentials I'm relying on right now ]]></title>
                                                                                                <dc:content><![CDATA[ <p>It's 10:30 on Monday as I write this, and it's already a crazy week, isn't it? The PM is headed off (again), and the Met Office app is already telling me we're in the late 20s celsius. </p><p>And it's only going to get worse. Where I am, near Bath Spa, it's forecast to hit 39C on Wednesday, with much of England's south in the same position.</p><p>Thankfully, I've got a few tricks up my sleeve. I'm the Senior Reviews Editor here at Tom's Guide, which means I get access to my fair share of tech. Ever since May, I've been calling in and testing products ready for the summer heat.</p><p>I'm not going to say I'm as cool as I'd like to be, but I'm certainly a lot more comfortable with this gear than without. And as a bonus, some of it is on sale for the <a href="https://www.tomsguide.com/uk/news/best-prime-day-deals-and-sales">Prime Day deals</a>. Here's my essential heatwave tech. </p><h2 id="beat-the-heat-my-essential-heatwave-gear">Beat the heat: My essential heatwave gear</h2><div class="product"><a data-dimension112="50d1b703-20ce-4241-adf2-1bc5e86d2497" data-action="Deal Block" data-label="Handheld 15,000RPM fan" data-dimension48="Handheld 15,000RPM fan" data-dimension25="£29.98" href="https://www.amazon.co.uk/Warmco-Portable-Handheld-Adjustable-Rechargeable/dp/B0F7KB3795" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1004px;"><p class="vanilla-image-block" style="padding-top:121.31%;"><img id="4hL6SWWidtvtFAt3VPWhDf" name="Warmco Mini Turbo Handheld fan" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4hL6SWWidtvtFAt3VPWhDf-1920-80.jpg" mos="" align="middle" fullscreen="" width="1004" height="1218" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/Warmco-Portable-Handheld-Adjustable-Rechargeable/dp/B0F7KB3795" target="_blank" rel="nofollow" data-dimension112="50d1b703-20ce-4241-adf2-1bc5e86d2497" data-action="Deal Block" data-label="Handheld 15,000RPM fan" data-dimension48="Handheld 15,000RPM fan" data-dimension25="£29.98">Handheld 15,000RPM fan: £29.98</a></strong><br>Let's start with the obvious: a fan! I bought this handheld Warmco fan a few weeks back to keep me cool during a humid press trip to Vienna. And it hasn't left my bag since. It's powerful, and on the lower settings (which still give plenty of cooling), it lasts for hours. It's now seeing me through a second heatwave, in addition to the Austria trip and a late May camping trip in mid-20s heat. It's a cheap alternative to the Shark ChillPill 3-in-1, although lacks the Shark's misting function. <a class="view-deal button" href="https://www.amazon.co.uk/Warmco-Portable-Handheld-Adjustable-Rechargeable/dp/B0F7KB3795" target="_blank" rel="nofollow" data-dimension112="50d1b703-20ce-4241-adf2-1bc5e86d2497" data-action="Deal Block" data-label="Handheld 15,000RPM fan" data-dimension48="Handheld 15,000RPM fan" data-dimension25="£29.98">View Deal</a></p></div><div class="product"><a data-dimension112="427d475a-663a-4f23-9c2c-3a6cd199c97b" data-action="Deal Block" data-label="Purifier Cool PC2" data-dimension48="Purifier Cool PC2" data-dimension25="£449" href="https://www.dyson.co.uk/air-treatment/purifiers/purifier-cool-pc2/de-nox-white-gold" 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="SKyi23fBfcUUzUYamXptpm" name="Dyson PC2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/SKyi23fBfcUUzUYamXptpm-1920-80.jpg" mos="" align="middle" fullscreen="" width="1080" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.dyson.co.uk/air-treatment/purifiers/purifier-cool-pc2/de-nox-white-gold" target="_blank" rel="nofollow" data-dimension112="427d475a-663a-4f23-9c2c-3a6cd199c97b" data-action="Deal Block" data-label="Purifier Cool PC2" data-dimension48="Purifier Cool PC2" data-dimension25="£449">Purifier Cool PC2: was £549 now £449</a></strong><br>Yep... another fan. But a much bigger one this time. I've been testing two <a href="https://www.tomsguide.com/home/home-appliances/dyson-find-follow-purifier-cool-pc3-review">Dyson Purifier Cool PC3</a> fans since our last heatwave a few weeks back, and these things are life-savers. The PC2 is essentially the same as the newer PC3, but cheaper, as it lacks the PC3's automatic human detection / tracking mode (which isn't that good anyway). What I love about the Dyson PC range is that they're fans and purifiers. That means if you have your windows open to let a breeze in, it'll also clean the air of pollen, dust and nasty road emissions.<a class="view-deal button" href="https://www.dyson.co.uk/air-treatment/purifiers/purifier-cool-pc2/de-nox-white-gold" target="_blank" rel="nofollow" data-dimension112="427d475a-663a-4f23-9c2c-3a6cd199c97b" data-action="Deal Block" data-label="Purifier Cool PC2" data-dimension48="Purifier Cool PC2" data-dimension25="£449">View Deal</a></p></div><div class="product"><a data-dimension112="b439de8e-8d6c-4839-baed-1581c228c656" data-action="Deal Block" data-label="REON Pocket 5" data-dimension48="REON Pocket 5" data-dimension25="£139" href="https://www.amazon.co.uk/REON-POCKET-Sensing-Wearable-Cooler/dp/B0CXCDZ1FW" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:300px;"><p class="vanilla-image-block" style="padding-top:88.00%;"><img id="FfS3JcFkD46HQTwH6krz3B" name="Sony REON Pocket 5" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FfS3JcFkD46HQTwH6krz3B-1920-80.jpg" mos="" align="middle" fullscreen="" width="300" height="264" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/REON-POCKET-Sensing-Wearable-Cooler/dp/B0CXCDZ1FW" target="_blank" rel="nofollow" data-dimension112="b439de8e-8d6c-4839-baed-1581c228c656" data-action="Deal Block" data-label="REON Pocket 5" data-dimension48="REON Pocket 5" data-dimension25="£139">REON Pocket 5: £139</a></strong><br>Another personal cooling device, the Sony REON goes about things a little differently. Instead of blowing air at you, it sits on the back of your neck (or wherever you want to place it) and cools down your whole body. We recently tested the <a href="https://www.tomsguide.com/home/reon-pocket-pro-plus-review">Sony REON Pocket Pro Plus</a> and awarded our Editor's Choice badge — these things are seriously good.<a class="view-deal button" href="https://www.amazon.co.uk/REON-POCKET-Sensing-Wearable-Cooler/dp/B0CXCDZ1FW" target="_blank" rel="nofollow" data-dimension112="b439de8e-8d6c-4839-baed-1581c228c656" data-action="Deal Block" data-label="REON Pocket 5" data-dimension48="REON Pocket 5" data-dimension25="£139">View Deal</a></p></div><div class="product"><a data-dimension112="64ceaca6-4ea6-4af9-97d9-14b72b003625" data-action="Deal Block" data-label="Freezer blocks (6-pack)" data-dimension48="Freezer blocks (6-pack)" data-dimension25="£5.99" href="https://www.amazon.co.uk/KEPLIN-Freezer-Family-Blocks-17x10x2cm/dp/B07CHWHPR4" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:256px;"><p class="vanilla-image-block" style="padding-top:117.19%;"><img id="hNhSXxZHgCJEWTJSBMVEZZ" name="Kelpin Freezer Blocks" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/hNhSXxZHgCJEWTJSBMVEZZ-1920-80.jpg" mos="" align="middle" fullscreen="" width="256" height="300" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/KEPLIN-Freezer-Family-Blocks-17x10x2cm/dp/B07CHWHPR4" target="_blank" rel="nofollow" data-dimension112="64ceaca6-4ea6-4af9-97d9-14b72b003625" data-action="Deal Block" data-label="Freezer blocks (6-pack)" data-dimension48="Freezer blocks (6-pack)" data-dimension25="£5.99">Freezer blocks (6-pack): £5.99</a></strong><br>If you aren't doing this hack already, you need to be. I may have access to loads of high-end gadgets, but sometimes the low-tech solutions are the best. Putting ice in front of a fan cools down the air it pushes through, creating a jerry-rigged AC system. It doesn't have to be complicated: you can simply use a bowl of ice if you like, but I prefer to use large, re-freezable ice packs like these. For under £6, this is a cheap, simple and reusable way to increase your fan's performance.<a class="view-deal button" href="https://www.amazon.co.uk/KEPLIN-Freezer-Family-Blocks-17x10x2cm/dp/B07CHWHPR4" target="_blank" rel="nofollow" data-dimension112="64ceaca6-4ea6-4af9-97d9-14b72b003625" data-action="Deal Block" data-label="Freezer blocks (6-pack)" data-dimension48="Freezer blocks (6-pack)" data-dimension25="£5.99">View Deal</a></p></div><div class="product"><a data-dimension112="bca5131b-6f87-471b-9561-62f73ca13946" data-action="Deal Block" data-label="Karu 2 Pizza Oven" data-dimension48="Karu 2 Pizza Oven" data-dimension25="£383.2" href="https://www.amazon.co.uk/Ooni-Karu-Pizza-Essentials-Bundle/dp/B0H4RPXNWC/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1400px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="ZWXfKDKexA6grx2C7HXLoN" name="Ooni Karu 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ZWXfKDKexA6grx2C7HXLoN-1920-80.jpg" mos="" align="middle" fullscreen="" width="1400" height="1400" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/Ooni-Karu-Pizza-Essentials-Bundle/dp/B0H4RPXNWC/" target="_blank" rel="nofollow" data-dimension112="bca5131b-6f87-471b-9561-62f73ca13946" data-action="Deal Block" data-label="Karu 2 Pizza Oven" data-dimension48="Karu 2 Pizza Oven" data-dimension25="£383.2">Karu 2 Pizza Oven: was £409 now £383.2</a></strong><br>"How is a pizza oven going to help me beat the heat?" Just... hear me out. I've been rinsing the pizzas for the last few weeks, in large part because cooking in my kitchen is absolutely dire in this heat. The Ooni needs to be run outside, and cooking outside stops your kitchen and house being heated up by the oven, hob or air fryer. Oh, and the Karu 2 is an awesome, affordable little oven which heats up quickly, can use various fuels, and cooks pro-quality pizzas in as little as 90 seconds. Dine al fresco.<a class="view-deal button" href="https://www.amazon.co.uk/Ooni-Karu-Pizza-Essentials-Bundle/dp/B0H4RPXNWC/" target="_blank" rel="nofollow" data-dimension112="bca5131b-6f87-471b-9561-62f73ca13946" data-action="Deal Block" data-label="Karu 2 Pizza Oven" data-dimension48="Karu 2 Pizza Oven" data-dimension25="£383.2">View Deal</a></p></div><div class="product"><a data-dimension112="90ef7b34-8055-4d69-a911-ce28ea2f41de" data-action="Deal Block" data-label="Heavy duty camping tarp" data-dimension48="Heavy duty camping tarp" data-dimension25="£46.99" href="https://www.amazon.co.uk/WADEO-Camping-Waterproof-Heavy-Duty-Hammock/dp/B0BLMJLMPR" 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:92.20%;"><img id="F9Xb5pu5sbgPvNVubsDvdi" name="WADEO Camping Tarp Shelter" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/F9Xb5pu5sbgPvNVubsDvdi-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="1383" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/WADEO-Camping-Waterproof-Heavy-Duty-Hammock/dp/B0BLMJLMPR" target="_blank" rel="nofollow" data-dimension112="90ef7b34-8055-4d69-a911-ce28ea2f41de" data-action="Deal Block" data-label="Heavy duty camping tarp" data-dimension48="Heavy duty camping tarp" data-dimension25="£46.99">Heavy duty camping tarp: £46.99</a></strong><br>No parasol? No problem. A camping tarp is a great budget alternative. I bought one like this Wadeo heavy duty tarpaulin, initially for camping, but I've put it up in my garden for the coming week to provide some more shade for my family and dogs. <a class="view-deal button" href="https://www.amazon.co.uk/WADEO-Camping-Waterproof-Heavy-Duty-Hammock/dp/B0BLMJLMPR" target="_blank" rel="nofollow" data-dimension112="90ef7b34-8055-4d69-a911-ce28ea2f41de" data-action="Deal Block" data-label="Heavy duty camping tarp" data-dimension48="Heavy duty camping tarp" data-dimension25="£46.99">View Deal</a></p></div><div class="product"><a data-dimension112="78ae8918-6a93-4837-bc3c-078016ef9fd4" data-action="Deal Block" data-label="Pet cooling mat" data-dimension48="Pet cooling mat" data-dimension25="£39.09" href="https://www.amazon.co.uk/Pecute-Cooling-Summer-Durable-Non-Toxic/dp/B08M478X8C" 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:49.40%;"><img id="Y3J4y2vtb5QJU3vSGwpr3J" name="Pecute pet cooling mat" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Y3J4y2vtb5QJU3vSGwpr3J-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="741" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/Pecute-Cooling-Summer-Durable-Non-Toxic/dp/B08M478X8C" target="_blank" rel="nofollow" data-dimension112="78ae8918-6a93-4837-bc3c-078016ef9fd4" data-action="Deal Block" data-label="Pet cooling mat" data-dimension48="Pet cooling mat" data-dimension25="£39.09">Pet cooling mat: was £47.99 now £39.09</a></strong><br>If, like me, you've got pets, you'll know that the severe heat brings an extra stressor: keeping your animals comfortable and safe from the heat. I have two large ex-racing greyhounds, who really struggle in the heat (especially at night). We use two large cooling pads, which are very effective. You can put them underneath a thin blanket if your pet isn't liking the look of the mat, and they'll still benefit from the cooling effect. Our dogs actually get a little chilly on these during the night, and go back to their fluffy warm beds in the early hours!<a class="view-deal button" href="https://www.amazon.co.uk/Pecute-Cooling-Summer-Durable-Non-Toxic/dp/B08M478X8C" target="_blank" rel="nofollow" data-dimension112="78ae8918-6a93-4837-bc3c-078016ef9fd4" data-action="Deal Block" data-label="Pet cooling mat" data-dimension48="Pet cooling mat" data-dimension25="£39.09">View Deal</a></p></div><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> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tech/who-needs-a-c-anyway-heatwave-essentials-im-relying-on-right-now</link>
                                                                            <description>
                            <![CDATA[ How a tech tester stays cool in 39C. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">rF8GEgEuWMPwJPCYMCbM3T</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/8yUAsJSJPjW4wRxAxWyv5X-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 22 Jun 2026 11:02:15 +0000</pubDate>                                                                                                                                <updated>Mon, 22 Jun 2026 11:27:45 +0000</updated>
                                                                                                                                            <category><![CDATA[Tech]]></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-320-70.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/8yUAsJSJPjW4wRxAxWyv5X-1920-80.jpg">
                                                            <media:credit><![CDATA[Peter Wolinski / Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A Warmco portable handheld fan in hand with green foliage in the background]]></media:description>                                                            <media:text><![CDATA[A Warmco portable handheld fan in hand with green foliage in the background]]></media:text>
                                <media:title type="plain"><![CDATA[A Warmco portable handheld fan in hand with green foliage in the background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/8yUAsJSJPjW4wRxAxWyv5X-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It's 10:30 on Monday as I write this, and it's already a crazy week, isn't it? The PM is headed off (again), and the Met Office app is already telling me we're in the late 20s celsius. </p><p>And it's only going to get worse. Where I am, near Bath Spa, it's forecast to hit 39C on Wednesday, with much of England's south in the same position.</p><p>Thankfully, I've got a few tricks up my sleeve. I'm the Senior Reviews Editor here at Tom's Guide, which means I get access to my fair share of tech. Ever since May, I've been calling in and testing products ready for the summer heat.</p><p>I'm not going to say I'm as cool as I'd like to be, but I'm certainly a lot more comfortable with this gear than without. And as a bonus, some of it is on sale for the <a href="https://www.tomsguide.com/uk/news/best-prime-day-deals-and-sales">Prime Day deals</a>. Here's my essential heatwave tech. </p><h2 id="beat-the-heat-my-essential-heatwave-gear">Beat the heat: My essential heatwave gear</h2><div class="product"><a data-dimension112="50d1b703-20ce-4241-adf2-1bc5e86d2497" data-action="Deal Block" data-label="Handheld 15,000RPM fan" data-dimension48="Handheld 15,000RPM fan" data-dimension25="£29.98" href="https://www.amazon.co.uk/Warmco-Portable-Handheld-Adjustable-Rechargeable/dp/B0F7KB3795" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1004px;"><p class="vanilla-image-block" style="padding-top:121.31%;"><img id="4hL6SWWidtvtFAt3VPWhDf" name="Warmco Mini Turbo Handheld fan" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4hL6SWWidtvtFAt3VPWhDf-1920-80.jpg" mos="" align="middle" fullscreen="" width="1004" height="1218" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/Warmco-Portable-Handheld-Adjustable-Rechargeable/dp/B0F7KB3795" target="_blank" rel="nofollow" data-dimension112="50d1b703-20ce-4241-adf2-1bc5e86d2497" data-action="Deal Block" data-label="Handheld 15,000RPM fan" data-dimension48="Handheld 15,000RPM fan" data-dimension25="£29.98">Handheld 15,000RPM fan: £29.98</a></strong><br>Let's start with the obvious: a fan! I bought this handheld Warmco fan a few weeks back to keep me cool during a humid press trip to Vienna. And it hasn't left my bag since. It's powerful, and on the lower settings (which still give plenty of cooling), it lasts for hours. It's now seeing me through a second heatwave, in addition to the Austria trip and a late May camping trip in mid-20s heat. It's a cheap alternative to the Shark ChillPill 3-in-1, although lacks the Shark's misting function. <a class="view-deal button" href="https://www.amazon.co.uk/Warmco-Portable-Handheld-Adjustable-Rechargeable/dp/B0F7KB3795" target="_blank" rel="nofollow" data-dimension112="50d1b703-20ce-4241-adf2-1bc5e86d2497" data-action="Deal Block" data-label="Handheld 15,000RPM fan" data-dimension48="Handheld 15,000RPM fan" data-dimension25="£29.98">View Deal</a></p></div><div class="product"><a data-dimension112="427d475a-663a-4f23-9c2c-3a6cd199c97b" data-action="Deal Block" data-label="Purifier Cool PC2" data-dimension48="Purifier Cool PC2" data-dimension25="£449" href="https://www.dyson.co.uk/air-treatment/purifiers/purifier-cool-pc2/de-nox-white-gold" 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="SKyi23fBfcUUzUYamXptpm" name="Dyson PC2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/SKyi23fBfcUUzUYamXptpm-1920-80.jpg" mos="" align="middle" fullscreen="" width="1080" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.dyson.co.uk/air-treatment/purifiers/purifier-cool-pc2/de-nox-white-gold" target="_blank" rel="nofollow" data-dimension112="427d475a-663a-4f23-9c2c-3a6cd199c97b" data-action="Deal Block" data-label="Purifier Cool PC2" data-dimension48="Purifier Cool PC2" data-dimension25="£449">Purifier Cool PC2: was £549 now £449</a></strong><br>Yep... another fan. But a much bigger one this time. I've been testing two <a href="https://www.tomsguide.com/home/home-appliances/dyson-find-follow-purifier-cool-pc3-review">Dyson Purifier Cool PC3</a> fans since our last heatwave a few weeks back, and these things are life-savers. The PC2 is essentially the same as the newer PC3, but cheaper, as it lacks the PC3's automatic human detection / tracking mode (which isn't that good anyway). What I love about the Dyson PC range is that they're fans and purifiers. That means if you have your windows open to let a breeze in, it'll also clean the air of pollen, dust and nasty road emissions.<a class="view-deal button" href="https://www.dyson.co.uk/air-treatment/purifiers/purifier-cool-pc2/de-nox-white-gold" target="_blank" rel="nofollow" data-dimension112="427d475a-663a-4f23-9c2c-3a6cd199c97b" data-action="Deal Block" data-label="Purifier Cool PC2" data-dimension48="Purifier Cool PC2" data-dimension25="£449">View Deal</a></p></div><div class="product"><a data-dimension112="b439de8e-8d6c-4839-baed-1581c228c656" data-action="Deal Block" data-label="REON Pocket 5" data-dimension48="REON Pocket 5" data-dimension25="£139" href="https://www.amazon.co.uk/REON-POCKET-Sensing-Wearable-Cooler/dp/B0CXCDZ1FW" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:300px;"><p class="vanilla-image-block" style="padding-top:88.00%;"><img id="FfS3JcFkD46HQTwH6krz3B" name="Sony REON Pocket 5" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FfS3JcFkD46HQTwH6krz3B-1920-80.jpg" mos="" align="middle" fullscreen="" width="300" height="264" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/REON-POCKET-Sensing-Wearable-Cooler/dp/B0CXCDZ1FW" target="_blank" rel="nofollow" data-dimension112="b439de8e-8d6c-4839-baed-1581c228c656" data-action="Deal Block" data-label="REON Pocket 5" data-dimension48="REON Pocket 5" data-dimension25="£139">REON Pocket 5: £139</a></strong><br>Another personal cooling device, the Sony REON goes about things a little differently. Instead of blowing air at you, it sits on the back of your neck (or wherever you want to place it) and cools down your whole body. We recently tested the <a href="https://www.tomsguide.com/home/reon-pocket-pro-plus-review">Sony REON Pocket Pro Plus</a> and awarded our Editor's Choice badge — these things are seriously good.<a class="view-deal button" href="https://www.amazon.co.uk/REON-POCKET-Sensing-Wearable-Cooler/dp/B0CXCDZ1FW" target="_blank" rel="nofollow" data-dimension112="b439de8e-8d6c-4839-baed-1581c228c656" data-action="Deal Block" data-label="REON Pocket 5" data-dimension48="REON Pocket 5" data-dimension25="£139">View Deal</a></p></div><div class="product"><a data-dimension112="64ceaca6-4ea6-4af9-97d9-14b72b003625" data-action="Deal Block" data-label="Freezer blocks (6-pack)" data-dimension48="Freezer blocks (6-pack)" data-dimension25="£5.99" href="https://www.amazon.co.uk/KEPLIN-Freezer-Family-Blocks-17x10x2cm/dp/B07CHWHPR4" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:256px;"><p class="vanilla-image-block" style="padding-top:117.19%;"><img id="hNhSXxZHgCJEWTJSBMVEZZ" name="Kelpin Freezer Blocks" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/hNhSXxZHgCJEWTJSBMVEZZ-1920-80.jpg" mos="" align="middle" fullscreen="" width="256" height="300" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/KEPLIN-Freezer-Family-Blocks-17x10x2cm/dp/B07CHWHPR4" target="_blank" rel="nofollow" data-dimension112="64ceaca6-4ea6-4af9-97d9-14b72b003625" data-action="Deal Block" data-label="Freezer blocks (6-pack)" data-dimension48="Freezer blocks (6-pack)" data-dimension25="£5.99">Freezer blocks (6-pack): £5.99</a></strong><br>If you aren't doing this hack already, you need to be. I may have access to loads of high-end gadgets, but sometimes the low-tech solutions are the best. Putting ice in front of a fan cools down the air it pushes through, creating a jerry-rigged AC system. It doesn't have to be complicated: you can simply use a bowl of ice if you like, but I prefer to use large, re-freezable ice packs like these. For under £6, this is a cheap, simple and reusable way to increase your fan's performance.<a class="view-deal button" href="https://www.amazon.co.uk/KEPLIN-Freezer-Family-Blocks-17x10x2cm/dp/B07CHWHPR4" target="_blank" rel="nofollow" data-dimension112="64ceaca6-4ea6-4af9-97d9-14b72b003625" data-action="Deal Block" data-label="Freezer blocks (6-pack)" data-dimension48="Freezer blocks (6-pack)" data-dimension25="£5.99">View Deal</a></p></div><div class="product"><a data-dimension112="bca5131b-6f87-471b-9561-62f73ca13946" data-action="Deal Block" data-label="Karu 2 Pizza Oven" data-dimension48="Karu 2 Pizza Oven" data-dimension25="£383.2" href="https://www.amazon.co.uk/Ooni-Karu-Pizza-Essentials-Bundle/dp/B0H4RPXNWC/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1400px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="ZWXfKDKexA6grx2C7HXLoN" name="Ooni Karu 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ZWXfKDKexA6grx2C7HXLoN-1920-80.jpg" mos="" align="middle" fullscreen="" width="1400" height="1400" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/Ooni-Karu-Pizza-Essentials-Bundle/dp/B0H4RPXNWC/" target="_blank" rel="nofollow" data-dimension112="bca5131b-6f87-471b-9561-62f73ca13946" data-action="Deal Block" data-label="Karu 2 Pizza Oven" data-dimension48="Karu 2 Pizza Oven" data-dimension25="£383.2">Karu 2 Pizza Oven: was £409 now £383.2</a></strong><br>"How is a pizza oven going to help me beat the heat?" Just... hear me out. I've been rinsing the pizzas for the last few weeks, in large part because cooking in my kitchen is absolutely dire in this heat. The Ooni needs to be run outside, and cooking outside stops your kitchen and house being heated up by the oven, hob or air fryer. Oh, and the Karu 2 is an awesome, affordable little oven which heats up quickly, can use various fuels, and cooks pro-quality pizzas in as little as 90 seconds. Dine al fresco.<a class="view-deal button" href="https://www.amazon.co.uk/Ooni-Karu-Pizza-Essentials-Bundle/dp/B0H4RPXNWC/" target="_blank" rel="nofollow" data-dimension112="bca5131b-6f87-471b-9561-62f73ca13946" data-action="Deal Block" data-label="Karu 2 Pizza Oven" data-dimension48="Karu 2 Pizza Oven" data-dimension25="£383.2">View Deal</a></p></div><div class="product"><a data-dimension112="90ef7b34-8055-4d69-a911-ce28ea2f41de" data-action="Deal Block" data-label="Heavy duty camping tarp" data-dimension48="Heavy duty camping tarp" data-dimension25="£46.99" href="https://www.amazon.co.uk/WADEO-Camping-Waterproof-Heavy-Duty-Hammock/dp/B0BLMJLMPR" 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:92.20%;"><img id="F9Xb5pu5sbgPvNVubsDvdi" name="WADEO Camping Tarp Shelter" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/F9Xb5pu5sbgPvNVubsDvdi-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="1383" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/WADEO-Camping-Waterproof-Heavy-Duty-Hammock/dp/B0BLMJLMPR" target="_blank" rel="nofollow" data-dimension112="90ef7b34-8055-4d69-a911-ce28ea2f41de" data-action="Deal Block" data-label="Heavy duty camping tarp" data-dimension48="Heavy duty camping tarp" data-dimension25="£46.99">Heavy duty camping tarp: £46.99</a></strong><br>No parasol? No problem. A camping tarp is a great budget alternative. I bought one like this Wadeo heavy duty tarpaulin, initially for camping, but I've put it up in my garden for the coming week to provide some more shade for my family and dogs. <a class="view-deal button" href="https://www.amazon.co.uk/WADEO-Camping-Waterproof-Heavy-Duty-Hammock/dp/B0BLMJLMPR" target="_blank" rel="nofollow" data-dimension112="90ef7b34-8055-4d69-a911-ce28ea2f41de" data-action="Deal Block" data-label="Heavy duty camping tarp" data-dimension48="Heavy duty camping tarp" data-dimension25="£46.99">View Deal</a></p></div><div class="product"><a data-dimension112="78ae8918-6a93-4837-bc3c-078016ef9fd4" data-action="Deal Block" data-label="Pet cooling mat" data-dimension48="Pet cooling mat" data-dimension25="£39.09" href="https://www.amazon.co.uk/Pecute-Cooling-Summer-Durable-Non-Toxic/dp/B08M478X8C" 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:49.40%;"><img id="Y3J4y2vtb5QJU3vSGwpr3J" name="Pecute pet cooling mat" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Y3J4y2vtb5QJU3vSGwpr3J-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="741" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.co.uk/Pecute-Cooling-Summer-Durable-Non-Toxic/dp/B08M478X8C" target="_blank" rel="nofollow" data-dimension112="78ae8918-6a93-4837-bc3c-078016ef9fd4" data-action="Deal Block" data-label="Pet cooling mat" data-dimension48="Pet cooling mat" data-dimension25="£39.09">Pet cooling mat: was £47.99 now £39.09</a></strong><br>If, like me, you've got pets, you'll know that the severe heat brings an extra stressor: keeping your animals comfortable and safe from the heat. I have two large ex-racing greyhounds, who really struggle in the heat (especially at night). We use two large cooling pads, which are very effective. You can put them underneath a thin blanket if your pet isn't liking the look of the mat, and they'll still benefit from the cooling effect. Our dogs actually get a little chilly on these during the night, and go back to their fluffy warm beds in the early hours!<a class="view-deal button" href="https://www.amazon.co.uk/Pecute-Cooling-Summer-Durable-Non-Toxic/dp/B08M478X8C" target="_blank" rel="nofollow" data-dimension112="78ae8918-6a93-4837-bc3c-078016ef9fd4" data-action="Deal Block" data-label="Pet cooling mat" data-dimension48="Pet cooling mat" data-dimension25="£39.09">View Deal</a></p></div><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[ Samsung, Sony, Bose, and Sonos have already launched Prime Day sales: Our highest-rated soundbars are up to $228 off right now ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I've sifted through the dregs of Amazon so you don't have to — <em>and</em> I've found savings for five of my highest-rated soundbars. As one of Tom's Guide's resident soundbar reviewers, I've tested enough of these home cinema bad boys to know exactly which ones are worth your time (and money). </p><p>It's not even Prime Day yet, and Samsung, Sony, <em>and</em> Bose have all slashed prices on some of their <a href="https://www.tomsguide.com/audio/soundbars/best-soundbars">best soundbars</a>, including the super-budget <a href="https://www.tomsguide.com/audio/soundbars/sony-ht-s100f-review">Sony HT-S100F</a>, <a href="https://www.tomsguide.com/audio/soundbars/bose-smart-soundbar-review">Bose Smart Soundbar</a> (usually $550!), and my personal favorite mid-range soundbar, <a href="https://www.tomsguide.com/audio/soundbars/samsung-q600f-review">Samsung Q600F</a>. I genuinely didn't believe this price when I saw it — <em>$371. </em>For a — really good — soundbar and subwoofer? Its launch price was $599! Is this real life? </p><p>I wouldn't wait for Prime Day with deals like this, but if you want to stick around for the Tom's Guide Savings Squad to save the day, we've selected the <a href="https://www.tomsguide.com/sales-events/prime-day-sneak-peek-ive-picked-41-amazon-deals-too-good-to-wait-for">best Prime Day deals</a> on everything from TVs to headphones to coffee makers to smart watches. </p><h2 class="article-body__section" id="section-best-early-prime-day-soundbar-deals-quick-links"><span>Best early Prime Day soundbar deals: Quick links</span></h2><ul><li><strong>Sony HT-S100F: </strong><a href="https://www.amazon.com/Sony-Speaker-Integrated-Bluetooth-HTS100F/dp/B0797MY6JB" target="_blank" rel="nofollow"><strong>was $118 now $98 @ Amazon </strong></a></li><li><strong>Klipsch Flexus Core 100: </strong><a href="https://www.amazon.com/Klipsch-100-Channel-Powered-Bluetooth/dp/B0CV5ZMDYY" target="_blank" rel="nofollow"><strong>was $349 now $249 @ Amazon</strong></a></li><li><strong>Sonos Beam Gen 2: </strong><a href="https://www.amazon.com/Sonos-Beam-Gen-2-Black/dp/B09GPYL7BJ" target="_blank" rel="nofollow"><strong>was $499 now $369 @ Amazon</strong></a></li><li><strong>Samsung Q600F: </strong><a href="https://www.amazon.com/Samsung-Soundbar-HW-Q600F-Wireless-Subwoofer/dp/B0GS4P3YL8" target="_blank" rel="nofollow"><strong>was $599 now $371 @ Amazon</strong></a></li><li><strong>Bose Smart Soundbar: </strong><a href="https://www.amazon.com/Bose-Soundbar-Bluetooth-Assistant-Capabilities/dp/B0D6W8XQ6J" target="_blank" rel="nofollow"><strong>was $549 now $399 @ Amazon</strong></a></li></ul><h2 class="article-body__section" id="section-best-early-prime-day-soundbar-deals"><span>Best early Prime Day soundbar deals</span></h2><div class="product"><a data-dimension112="2d3e71b2-e7eb-47cd-af87-012cd3d2c86d" data-action="Deal Block" data-label="HT-S100F" data-dimension48="HT-S100F" data-dimension25="$98" href="https://www.amazon.com/Sony-Speaker-Integrated-Bluetooth-HTS100F/dp/B0797MY6JB" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="PMZLt3HzCP9i43jUL74LZS" name="Sony-Soundbar" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/PMZLt3HzCP9i43jUL74LZS-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/audio/soundbars/sony-ht-s100f-review" data-dimension112="2d3e71b2-e7eb-47cd-af87-012cd3d2c86d" data-action="Deal Block" data-label="HT-S100F" data-dimension48="HT-S100F" data-dimension25="$98">Sony HT-S100F</a> (also called simply Sony S100F) is the best budget soundbar you can get nowadays, and there's very little competition. At launch, this soundbar was $99, but now it's usually around $119. Even so, when you can find it back at its pre-tariff price, it's definitely worth the investment. You get super clear dialogue and immersive sound for under $100.<a class="view-deal button" href="https://www.amazon.com/Sony-Speaker-Integrated-Bluetooth-HTS100F/dp/B0797MY6JB" target="_blank" rel="nofollow" data-dimension112="2d3e71b2-e7eb-47cd-af87-012cd3d2c86d" data-action="Deal Block" data-label="HT-S100F" data-dimension48="HT-S100F" data-dimension25="$98">View Deal</a></p></div><div class="product"><a data-dimension112="ce99bb02-06c5-4714-b09c-3d1cc489f81a" data-action="Deal Block" data-label="Flexus Core 100" data-dimension48="Flexus Core 100" data-dimension25="$249" href="https://www.amazon.com/Klipsch-100-Channel-Powered-Bluetooth/dp/B0CV5ZMDYY" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="Kst3T7bzZk2GeVeyZe7e5j" name="klipsch flexus core 100 deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Kst3T7bzZk2GeVeyZe7e5j-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/audio/soundbars/klipsch-flexus-core-100-review" data-dimension112="ce99bb02-06c5-4714-b09c-3d1cc489f81a" data-action="Deal Block" data-label="Flexus Core 100" data-dimension48="Flexus Core 100" data-dimension25="$249">Klipsch Flexus Core 100</a> is one of the best soundbars if you want to build out a whole sound system. You can start with Klipsch's entry-level soundbar (this one) and slowly work your way up with a sub and rears. The Core 100 has powerful bass and clear dialogue, making it ideal for movie nights and also reality TV binging. <a class="view-deal button" href="https://www.amazon.com/Klipsch-100-Channel-Powered-Bluetooth/dp/B0CV5ZMDYY" target="_blank" rel="nofollow" data-dimension112="ce99bb02-06c5-4714-b09c-3d1cc489f81a" data-action="Deal Block" data-label="Flexus Core 100" data-dimension48="Flexus Core 100" data-dimension25="$249">View Deal</a></p></div><div class="product"><a data-dimension112="0e7ef59b-205f-4723-b48e-37bb6aa6fa4d" data-action="Deal Block" data-label="Beam Gen 2" data-dimension48="Beam Gen 2" data-dimension25="$369" href="https://www.amazon.com/Sonos-Beam-Gen-2-Black/dp/B09GPYL7BJ" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="DAZRapi9fiu7HcN3m39JNd" name="Sonos Beam Gen 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/DAZRapi9fiu7HcN3m39JNd-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>While I haven't personally reviewed the <a href="https://www.tomsguide.com/reviews/sonos-beam-gen-2" data-dimension112="0e7ef59b-205f-4723-b48e-37bb6aa6fa4d" data-action="Deal Block" data-label="Beam Gen 2" data-dimension48="Beam Gen 2" data-dimension25="$369">Sonos Beam Gen 2</a>, we gave it a lovely 4-star review when it first released in 2021. Even though this model is 5 years old, it still holds its own and is ideal for smaller TVs. It has fantastic Dolby Atmos and punchy bass, which makes it sound more expensive than it is. <a class="view-deal button" href="https://www.amazon.com/Sonos-Beam-Gen-2-Black/dp/B09GPYL7BJ" target="_blank" rel="nofollow" data-dimension112="0e7ef59b-205f-4723-b48e-37bb6aa6fa4d" data-action="Deal Block" data-label="Beam Gen 2" data-dimension48="Beam Gen 2" data-dimension25="$369">View Deal</a></p></div><div class="product"><a data-dimension112="8cf0b3b9-d67e-436a-82ea-122584c6fb1e" data-action="Deal Block" data-label="Q600F" data-dimension48="Q600F" data-dimension25="$371" href="https://www.amazon.com/Samsung-Soundbar-HW-Q600F-Wireless-Subwoofer/dp/B0GS4P3YL8" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="ExfR2TSGT2maYw6G6JSFS5" name="Samsung Q600F" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ExfR2TSGT2maYw6G6JSFS5-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>I loved the <a href="https://www.tomsguide.com/audio/soundbars/samsung-q600f-review" data-dimension112="8cf0b3b9-d67e-436a-82ea-122584c6fb1e" data-action="Deal Block" data-label="Q600F" data-dimension48="Q600F" data-dimension25="$371">Samsung Q600F</a> when I reviewed it last year. This soundbar + subwoofer was <em>already</em> affordable at its launch price of $599, but now it's almost incomprehensibly cheap at $371. I'm not sure why the Amazon page lists its MSRP as $399 — it is most definitely $599. Although this is last year's model now — Samsung refreshes its soundbar lineup every year — I can assure you it sounds fantastic. You get immersive Dolby Atmos and crystal-clear dialogue for just $371... I know I'd be getting this one. <a class="view-deal button" href="https://www.amazon.com/Samsung-Soundbar-HW-Q600F-Wireless-Subwoofer/dp/B0GS4P3YL8" target="_blank" rel="nofollow" data-dimension112="8cf0b3b9-d67e-436a-82ea-122584c6fb1e" data-action="Deal Block" data-label="Q600F" data-dimension48="Q600F" data-dimension25="$371">View Deal</a></p></div><div class="product"><a data-dimension112="9d784100-33db-4ba0-8dff-b6ba1832912f" data-action="Deal Block" data-label="Smart Soundbar" data-dimension48="Smart Soundbar" data-dimension25="$399" href="https://www.amazon.com/Bose-Soundbar-Bluetooth-Assistant-Capabilities/dp/B0D6W8XQ6J" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="XT9aaiGZoDf6MtibTHXgr6" name="bose smart soundbar" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/XT9aaiGZoDf6MtibTHXgr6-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>I have the <a href="https://www.tomsguide.com/audio/soundbars/bose-smart-soundbar-review" data-dimension112="9d784100-33db-4ba0-8dff-b6ba1832912f" data-action="Deal Block" data-label="Smart Soundbar" data-dimension48="Smart Soundbar" data-dimension25="$399">Bose Smart Soundbar</a> at home and I love it. This compact soundbar is ideal for small living rooms without skimping on sound quality. Music playback is surprisingly great and AI Dialogue Mode means I never have to rewind due to missed speech. <a class="view-deal button" href="https://www.amazon.com/Bose-Soundbar-Bluetooth-Assistant-Capabilities/dp/B0D6W8XQ6J" target="_blank" rel="nofollow" data-dimension112="9d784100-33db-4ba0-8dff-b6ba1832912f" data-action="Deal Block" data-label="Smart Soundbar" data-dimension48="Smart Soundbar" data-dimension25="$399">View Deal</a></p></div><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-soundbar-deals"><span>We're tracking all the best Prime Day soundbar deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Soundbars&min_discount_ratio=0.95&retailer=Amazon&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> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/soundbars/samsung-sony-bose-and-sonos-have-already-launched-prime-day-sales-our-highest-rated-soundbars-are-up-to-usd228-off-right-now</link>
                                                                            <description>
                            <![CDATA[ I wouldn't wait until Prime Day for these soundbar deals. I've found Bose, Sony, Samsung, Klipsch and Sonos savings on some of their best soundbars. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">MYPyVEfXx2CE5ggb63gWsg</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/wmsvJwYR482seAibhPpppk-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 22 Jun 2026 09:15:12 +0000</pubDate>                                                                                                                                <updated>Mon, 22 Jun 2026 09:49:41 +0000</updated>
                                                                                                                                            <category><![CDATA[Soundbars]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Speakers]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/wmsvJwYR482seAibhPpppk-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the samsung q600f prime day hero image with the tom&#039;s guide savings squad badge]]></media:description>                                                            <media:text><![CDATA[the samsung q600f prime day hero image with the tom&#039;s guide savings squad badge]]></media:text>
                                <media:title type="plain"><![CDATA[the samsung q600f prime day hero image with the tom&#039;s guide savings squad badge]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/wmsvJwYR482seAibhPpppk-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I've sifted through the dregs of Amazon so you don't have to — <em>and</em> I've found savings for five of my highest-rated soundbars. As one of Tom's Guide's resident soundbar reviewers, I've tested enough of these home cinema bad boys to know exactly which ones are worth your time (and money). </p><p>It's not even Prime Day yet, and Samsung, Sony, <em>and</em> Bose have all slashed prices on some of their <a href="https://www.tomsguide.com/audio/soundbars/best-soundbars">best soundbars</a>, including the super-budget <a href="https://www.tomsguide.com/audio/soundbars/sony-ht-s100f-review">Sony HT-S100F</a>, <a href="https://www.tomsguide.com/audio/soundbars/bose-smart-soundbar-review">Bose Smart Soundbar</a> (usually $550!), and my personal favorite mid-range soundbar, <a href="https://www.tomsguide.com/audio/soundbars/samsung-q600f-review">Samsung Q600F</a>. I genuinely didn't believe this price when I saw it — <em>$371. </em>For a — really good — soundbar and subwoofer? Its launch price was $599! Is this real life? </p><p>I wouldn't wait for Prime Day with deals like this, but if you want to stick around for the Tom's Guide Savings Squad to save the day, we've selected the <a href="https://www.tomsguide.com/sales-events/prime-day-sneak-peek-ive-picked-41-amazon-deals-too-good-to-wait-for">best Prime Day deals</a> on everything from TVs to headphones to coffee makers to smart watches. </p><h2 class="article-body__section" id="section-best-early-prime-day-soundbar-deals-quick-links"><span>Best early Prime Day soundbar deals: Quick links</span></h2><ul><li><strong>Sony HT-S100F: </strong><a href="https://www.amazon.com/Sony-Speaker-Integrated-Bluetooth-HTS100F/dp/B0797MY6JB" target="_blank" rel="nofollow"><strong>was $118 now $98 @ Amazon </strong></a></li><li><strong>Klipsch Flexus Core 100: </strong><a href="https://www.amazon.com/Klipsch-100-Channel-Powered-Bluetooth/dp/B0CV5ZMDYY" target="_blank" rel="nofollow"><strong>was $349 now $249 @ Amazon</strong></a></li><li><strong>Sonos Beam Gen 2: </strong><a href="https://www.amazon.com/Sonos-Beam-Gen-2-Black/dp/B09GPYL7BJ" target="_blank" rel="nofollow"><strong>was $499 now $369 @ Amazon</strong></a></li><li><strong>Samsung Q600F: </strong><a href="https://www.amazon.com/Samsung-Soundbar-HW-Q600F-Wireless-Subwoofer/dp/B0GS4P3YL8" target="_blank" rel="nofollow"><strong>was $599 now $371 @ Amazon</strong></a></li><li><strong>Bose Smart Soundbar: </strong><a href="https://www.amazon.com/Bose-Soundbar-Bluetooth-Assistant-Capabilities/dp/B0D6W8XQ6J" target="_blank" rel="nofollow"><strong>was $549 now $399 @ Amazon</strong></a></li></ul><h2 class="article-body__section" id="section-best-early-prime-day-soundbar-deals"><span>Best early Prime Day soundbar deals</span></h2><div class="product"><a data-dimension112="2d3e71b2-e7eb-47cd-af87-012cd3d2c86d" data-action="Deal Block" data-label="HT-S100F" data-dimension48="HT-S100F" data-dimension25="$98" href="https://www.amazon.com/Sony-Speaker-Integrated-Bluetooth-HTS100F/dp/B0797MY6JB" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="PMZLt3HzCP9i43jUL74LZS" name="Sony-Soundbar" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/PMZLt3HzCP9i43jUL74LZS-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/audio/soundbars/sony-ht-s100f-review" data-dimension112="2d3e71b2-e7eb-47cd-af87-012cd3d2c86d" data-action="Deal Block" data-label="HT-S100F" data-dimension48="HT-S100F" data-dimension25="$98">Sony HT-S100F</a> (also called simply Sony S100F) is the best budget soundbar you can get nowadays, and there's very little competition. At launch, this soundbar was $99, but now it's usually around $119. Even so, when you can find it back at its pre-tariff price, it's definitely worth the investment. You get super clear dialogue and immersive sound for under $100.<a class="view-deal button" href="https://www.amazon.com/Sony-Speaker-Integrated-Bluetooth-HTS100F/dp/B0797MY6JB" target="_blank" rel="nofollow" data-dimension112="2d3e71b2-e7eb-47cd-af87-012cd3d2c86d" data-action="Deal Block" data-label="HT-S100F" data-dimension48="HT-S100F" data-dimension25="$98">View Deal</a></p></div><div class="product"><a data-dimension112="ce99bb02-06c5-4714-b09c-3d1cc489f81a" data-action="Deal Block" data-label="Flexus Core 100" data-dimension48="Flexus Core 100" data-dimension25="$249" href="https://www.amazon.com/Klipsch-100-Channel-Powered-Bluetooth/dp/B0CV5ZMDYY" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="Kst3T7bzZk2GeVeyZe7e5j" name="klipsch flexus core 100 deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Kst3T7bzZk2GeVeyZe7e5j-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/audio/soundbars/klipsch-flexus-core-100-review" data-dimension112="ce99bb02-06c5-4714-b09c-3d1cc489f81a" data-action="Deal Block" data-label="Flexus Core 100" data-dimension48="Flexus Core 100" data-dimension25="$249">Klipsch Flexus Core 100</a> is one of the best soundbars if you want to build out a whole sound system. You can start with Klipsch's entry-level soundbar (this one) and slowly work your way up with a sub and rears. The Core 100 has powerful bass and clear dialogue, making it ideal for movie nights and also reality TV binging. <a class="view-deal button" href="https://www.amazon.com/Klipsch-100-Channel-Powered-Bluetooth/dp/B0CV5ZMDYY" target="_blank" rel="nofollow" data-dimension112="ce99bb02-06c5-4714-b09c-3d1cc489f81a" data-action="Deal Block" data-label="Flexus Core 100" data-dimension48="Flexus Core 100" data-dimension25="$249">View Deal</a></p></div><div class="product"><a data-dimension112="0e7ef59b-205f-4723-b48e-37bb6aa6fa4d" data-action="Deal Block" data-label="Beam Gen 2" data-dimension48="Beam Gen 2" data-dimension25="$369" href="https://www.amazon.com/Sonos-Beam-Gen-2-Black/dp/B09GPYL7BJ" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="DAZRapi9fiu7HcN3m39JNd" name="Sonos Beam Gen 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/DAZRapi9fiu7HcN3m39JNd-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>While I haven't personally reviewed the <a href="https://www.tomsguide.com/reviews/sonos-beam-gen-2" data-dimension112="0e7ef59b-205f-4723-b48e-37bb6aa6fa4d" data-action="Deal Block" data-label="Beam Gen 2" data-dimension48="Beam Gen 2" data-dimension25="$369">Sonos Beam Gen 2</a>, we gave it a lovely 4-star review when it first released in 2021. Even though this model is 5 years old, it still holds its own and is ideal for smaller TVs. It has fantastic Dolby Atmos and punchy bass, which makes it sound more expensive than it is. <a class="view-deal button" href="https://www.amazon.com/Sonos-Beam-Gen-2-Black/dp/B09GPYL7BJ" target="_blank" rel="nofollow" data-dimension112="0e7ef59b-205f-4723-b48e-37bb6aa6fa4d" data-action="Deal Block" data-label="Beam Gen 2" data-dimension48="Beam Gen 2" data-dimension25="$369">View Deal</a></p></div><div class="product"><a data-dimension112="8cf0b3b9-d67e-436a-82ea-122584c6fb1e" data-action="Deal Block" data-label="Q600F" data-dimension48="Q600F" data-dimension25="$371" href="https://www.amazon.com/Samsung-Soundbar-HW-Q600F-Wireless-Subwoofer/dp/B0GS4P3YL8" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="ExfR2TSGT2maYw6G6JSFS5" name="Samsung Q600F" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ExfR2TSGT2maYw6G6JSFS5-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>I loved the <a href="https://www.tomsguide.com/audio/soundbars/samsung-q600f-review" data-dimension112="8cf0b3b9-d67e-436a-82ea-122584c6fb1e" data-action="Deal Block" data-label="Q600F" data-dimension48="Q600F" data-dimension25="$371">Samsung Q600F</a> when I reviewed it last year. This soundbar + subwoofer was <em>already</em> affordable at its launch price of $599, but now it's almost incomprehensibly cheap at $371. I'm not sure why the Amazon page lists its MSRP as $399 — it is most definitely $599. Although this is last year's model now — Samsung refreshes its soundbar lineup every year — I can assure you it sounds fantastic. You get immersive Dolby Atmos and crystal-clear dialogue for just $371... I know I'd be getting this one. <a class="view-deal button" href="https://www.amazon.com/Samsung-Soundbar-HW-Q600F-Wireless-Subwoofer/dp/B0GS4P3YL8" target="_blank" rel="nofollow" data-dimension112="8cf0b3b9-d67e-436a-82ea-122584c6fb1e" data-action="Deal Block" data-label="Q600F" data-dimension48="Q600F" data-dimension25="$371">View Deal</a></p></div><div class="product"><a data-dimension112="9d784100-33db-4ba0-8dff-b6ba1832912f" data-action="Deal Block" data-label="Smart Soundbar" data-dimension48="Smart Soundbar" data-dimension25="$399" href="https://www.amazon.com/Bose-Soundbar-Bluetooth-Assistant-Capabilities/dp/B0D6W8XQ6J" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="XT9aaiGZoDf6MtibTHXgr6" name="bose smart soundbar" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/XT9aaiGZoDf6MtibTHXgr6-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>I have the <a href="https://www.tomsguide.com/audio/soundbars/bose-smart-soundbar-review" data-dimension112="9d784100-33db-4ba0-8dff-b6ba1832912f" data-action="Deal Block" data-label="Smart Soundbar" data-dimension48="Smart Soundbar" data-dimension25="$399">Bose Smart Soundbar</a> at home and I love it. This compact soundbar is ideal for small living rooms without skimping on sound quality. Music playback is surprisingly great and AI Dialogue Mode means I never have to rewind due to missed speech. <a class="view-deal button" href="https://www.amazon.com/Bose-Soundbar-Bluetooth-Assistant-Capabilities/dp/B0D6W8XQ6J" target="_blank" rel="nofollow" data-dimension112="9d784100-33db-4ba0-8dff-b6ba1832912f" data-action="Deal Block" data-label="Smart Soundbar" data-dimension48="Smart Soundbar" data-dimension25="$399">View Deal</a></p></div><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-soundbar-deals"><span>We're tracking all the best Prime Day soundbar deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Soundbars&min_discount_ratio=0.95&retailer=Amazon&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[ Sony’s new Bravia 7 II has a secret weapon that makes it so good ]]></title>
                                                                                                <dc:content><![CDATA[ <p>After six months of lab testing the year's newest TVs, it has become clear that the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html"><u>best TVs of 2026</u></a> are pushing brightness and color accuracy to new heights. Today’s flagship TVs from Hisense and TCL deliver good performance, thanks in large part to a backlight technology called Mini-RGB that’s changing the game for picture quality. </p><p>Yet Sony isn't sitting on the sidelines. Its new <a href="https://www.tomsguide.com/tvs/i-saw-sonys-true-rgb-up-close-and-the-downfall-of-oled-tvs-might-come-sooner-than-expected"><u>"True RGB" technology</u></a> also promises independently controlled red, green, and blue LEDs under the display panel but, according to Sony, offers “purer color expression, improved light steering, and ultra-wide viewing angles.” The best part? You don’t need to empty your pockets to get a taste. Despite it being used in the flagship <a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs"><u>Bravia 9 II</u></a> sibling, you can get it on the Bravia 7 II at a lower price.</p><p>But does Sony’s new tech actually live up to the hype? After putting the Sony Bravia 7 II through our rigorous lab testing workflow and spending two days watching real-world content, the results didn’t blow my expectations out of the water, but I’m still very impressed.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-pricing-and-availability"><span>Sony Bravia 7 II review: Pricing and availability</span></h3><p>The Bravia 7 II is  $300 more than the starting price of the 65-inch <a href="https://www.tomsguide.com/tvs/sony-bravia-7-tv-review"><u>Sony Bravia 7</u></a> we reviewed in 2025. Pricing for the Sony Bravia 7 II starts at $1,600 / AU$2,699 for the<a href="https://electronics.sony.com/tv-video/televisions/all-tvs/p/k50xr70m2"> <u>50-inch variant</u></a>, and goes as high as $9,000 / AU$12,999 for the 98-inch model. </p><p>MSRPs / RRPs for the whole range are listed below: </p><ul><li>50-inch: $1,599 / AU$2,699</li><li>55-inch: $2,099 / N/A</li><li>65-inch: $2,599 / AU$3,999</li><li>75-inch: $3,099 / AU$5,499</li><li>85-inch: $3,999 / AU$7,499</li><li>98-inch: $8,999 / AU$12,999</li></ul><p>The 65-inch review unit we have comes in at $2,600 / AU$3,999. Sony’s flagship Bravia 9 II costs $3,600 / AU$5,999, so if you buy the Bravia 7 II, you can save an extra $1,000 / AU$2,000. Stepping down means sacrificing a handful of dimming zones, the anti-glare features, and slightly more premium audio features — but these compromises aren’t so bad given the price difference.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="FAg72YbFiogDCcq3a3DLmf" name="Sony-Bravia-7-II--17" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/FAg72YbFiogDCcq3a3DLmf-1920-80.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>That said, the 7 II is venturing close to OLED pricing territory. The <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled-review"><u>LG C6 OLED </u></a>currently sits at $2,700, while the <a href="https://www.tomsguide.com/tvs/4k-tvs/lg-b6-oled-tv-review"><u>LG B6</u></a> costs roughly $2,000. TCL’s RGB offering, the <a href="https://www.tomsguide.com/tvs/qled-tvs/tcl-sqd-mini-led-qm8l-75-inch-tv-review"><u>QM8L</u></a>, has a price tag of $3,000 for the 75-inch model while the <a href="https://www.tomsguide.com/tvs/qled-tvs/hisense-ur9-rgb-mini-led-tv-review"><u>Hisense UR9</u></a> costs $2,000 for a 65-inch screen after the nearly unheard of $1,500 discount it received immediately after launch.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-design-and-ports"><span>Sony Bravia 7 II review: Design and ports</span></h3><p>The Sony Bravia 7 II immediately felt different to me the moment I took it out of the box, largely due to its redesigned center-facing stand. As part of the setup, you slot a clear glass-like sheet  into the front of the stand. Sony calls this the “Mirage Stand.” This helps hide the cables that might be behind the TV. It also helps light and color pass through. </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="K3JdjXdoJhZ6NFzr74k2jf" name="Sony-Bravia-7-II--22" alt="Sony Bravia 7 II True RGB TV pedestal" src="https://cdn.mos.cms.futurecdn.net/K3JdjXdoJhZ6NFzr74k2jf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The assembly was easy, involving building out both sides of the base, and then simply sliding the entire TV into place and fastening it with screws. Compared to my experience with setting up other TVs, the setup feels remarkably seamless. I wish other manufacturers would embrace this approach. The trade-off is that, on the sides, the Bravia 7 II is still slightly thick, similar to other RGB TVs in this class.</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="udLZDxWgK3osJPjsSfTrhf" name="Sony-Bravia-7-II--20" alt="Sony Bravia 7 II True RGB TV screen thickness" src="https://cdn.mos.cms.futurecdn.net/udLZDxWgK3osJPjsSfTrhf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The other issue besides its thickness is that Sony skimped out on ports: On the left side of the TV, there are four HDMI ports, just like last year’s model, but two of them are the older HDMI 2.0b standard. (HDMI 1 and 2 are HDMI 2.0b, while HDMI 3 and 4 are HDMI 2.1.) </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="VSxBS9GT5KHaZdTGTPUanf" name="Sony-Bravia-7-II--24" alt="Sony Bravia 7 II True RGB TV ports" src="https://cdn.mos.cms.futurecdn.net/VSxBS9GT5KHaZdTGTPUanf-1920-80.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>Those HDMI ports are all in addition to the two USB ports, optical audio out, S-Center speaker in, Ethernet, RS-232C remote, IR in, and a spot for a coaxial cable. </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="HdgvWQo6iku5Je3wgGGvnf" name="Sony-Bravia-7-II--18" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/HdgvWQo6iku5Je3wgGGvnf-1920-80.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>Finally, around the back, the non-detachable power cable is on the right rear. In that same area, Sony also gives you a spot to clip that cable to better help manage wires. The TV can be wall-mounted using the VESA holes on the back panel, should you prefer that.</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="wdvUHJiMrCkBhamxYSeuff" name="Sony-Bravia-7-II--21" alt="Sony Bravia 7 II True RGB TV pedestal" src="https://cdn.mos.cms.futurecdn.net/wdvUHJiMrCkBhamxYSeuff-1920-80.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>My biggest gripe with the 7 II’s design is that, for this price, it should have HDMI 2.1 ports standard. If you own multiple game consoles like I do, you’ll only be able to fully enjoy them at 120Hz on two of this TV's four ports. It’s not a deal breaker, but it is a small disappointment.</p><h3 class="article-body__section" id="section-how-we-test"><span>How we test</span></h3><p>Our TV review process at Tom's Guide for TVs is rigorous, but standardized. In our New York City test lab, my lab assistant and I test every TV across SDR and HDR benchmarks using the industry standard 10% window.</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="W3sBn7HXPCUfJ7jVg7F6pf" name="Sony-Bravia-7-II--16" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/W3sBn7HXPCUfJ7jVg7F6pf-1920-80.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>To ensure we are seeing the director's true vision, we switch the TV over to Filmmaker Mode which is typically the most color-accurate setting available. We also disable any AI features, Eco Mode, and any intrusive ambient light sensors that could impact readings.</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="MZnR9EJAfysrvkb29vAwmf" name="Sony-Bravia-7-II--15" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/MZnR9EJAfysrvkb29vAwmf-1920-80.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>We use the Jeti spectraval 1501-HiRes spectroradiometer to profile our Klein K10-A colorimeter. Once profiled, this high-end hardware works in tandem with a Murideo 8K-SIX-G Metal pattern generator and a custom workflow in <a href="https://www.portrait.com/calman-home/"><u>Portrait Displays Calman Ultimate software</u></a> to map out the TV’s performance. </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="gYtxBEtsg7bmJkuVDqaejf" name="Sony-Bravia-7-II--14" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/gYtxBEtsg7bmJkuVDqaejf-1920-80.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>For gaming performance, we use a Leo Bodnar 4K Video Signal Lag Tester to measure exactly how responsive the TV really is. Once technical lab testing is done, we put the TV through subjective testing using a curated selection of films and shows. For more on this process, read our “<a href="https://www.tomsguide.com/us/how-we-test-tvs,review-2478.html"><u>How we test TVs</u></a>” guide.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-performance-and-test-results"><span>Sony Bravia 7 II review: Performance and test results</span></h3><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Sony Bravia 7 II</strong></p></td><td  ><p><strong>Sony Bravia 7</strong></p></td><td  ><p><strong>Hisense UR9</strong></p></td><td  ><p><strong>TCL QM8L</strong></p></td><td  ><p><strong>LG C6</strong></p></td><td  ><p><strong>Samsung QN90F</strong></p></td></tr><tr><td class="firstcol " ><p><strong>SDR Brightness (10%, in nits)</strong></p></td><td  ><p>100</p></td><td  ><p>101</p></td><td  ><p>2,486</p></td><td  ><p>229</p></td><td  ><p>355</p></td><td  ><p>228</p></td></tr><tr><td class="firstcol " ><p><strong>Delta-E (lower is better)</strong></p></td><td  ><p>4.79</p></td><td  ><p>1.58</p></td><td  ><p>2.72</p></td><td  ><p>3.74</p></td><td  ><p>1.53</p></td><td  ><p>1.83</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 709 Gamut Coverage</strong></p></td><td  ><p>99.94%</p></td><td  ><p>99.62%</p></td><td  ><p>99.93%</p></td><td  ><p>99.55%</p></td><td  ><p>97.8%</p></td><td  ><p>99.15%</p></td></tr><tr><td class="firstcol " ><p><strong>HDR Brightness (10%, in nits)</strong></p></td><td  ><p>2,077</p></td><td  ><p>1,355</p></td><td  ><p>3,327</p></td><td  ><p>3,719</p></td><td  ><p>1,355</p></td><td  ><p>2,109</p></td></tr><tr><td class="firstcol " ><p><strong>UHDA-P3 Gamut Coverage</strong></p></td><td  ><p>97.73%</p></td><td  ><p>97.09%</p></td><td  ><p>99.83%</p></td><td  ><p>97.97%</p></td><td  ><p>99.45%</p></td><td  ><p>99.32%</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 2020 Gamut Coverage</strong></p></td><td  ><p>87.17%</p></td><td  ><p>78.61%</p></td><td  ><p>93.03%</p></td><td  ><p>90.34%</p></td><td  ><p>76.18%</p></td><td  ><p>77.44%</p></td></tr><tr><td class="firstcol " ><p><strong>Input latency (in milliseconds)</strong></p></td><td  ><p>10.4</p></td><td  ><p>17.1</p></td><td  ><p>12</p></td><td  ><p>9.5</p></td><td  ><p>12.9/9.1</p></td><td  ><p>9.5</p><p><br></p></td></tr></tbody></table></div><p>HDR performance is where the Bravia 7 II comes into its own league, with the peak brightness reaching 2,077 nits with our 10% test window. This is far ahead of what we've seen on OLED TVs like LG C6 and nearly double that of the original Bravia 7 model. However, it’s overshadowed by the Hisense UR9 and TCL QM8L.</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="TffrVPdDz5iRyeeE84Hggf" name="Sony-Bravia-7-II--6" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/TffrVPdDz5iRyeeE84Hggf-1920-80.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>When watching scenes in HDR  from <em>“The Batman”</em> on our lab’s Blu-ray player, I immediately noticed the highlights from headlights and explosions against the shadows of the night. Yet despite all of this, the black in Batman’s suit and the Batmobile remained true. </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="JeLhmPPKVvnD9ew2Lbrojf" name="Sony-Bravia-7-II--10" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/JeLhmPPKVvnD9ew2Lbrojf-1920-80.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>Streaming <em>“Tom Clancy’s Jack Ryan Ghost War”</em> on Amazon Prime, my initial impressions were corroborated: The TV rendered some really deep blacks and precisely managed highlights, ensuring that the riverside lamps in the introductory sequence showed absolutely no blooming against a black dark sky. This carried over into the film's climactic final battle, where even in dark night scenes, I could not spot any light bleed around the vibrant orange muzzle flashes or the crisp white LED signs on a building set against the dark night.</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="2L4yXSZEu2MuP994fuqunf" name="Sony-Bravia-7-II--9" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/2L4yXSZEu2MuP994fuqunf-1920-80.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>For SDR performance, Bravia TVs come with conservative brightness settings out of the box. As a result, SDR brightness measured just 100 nits in our lab testing for the TV’s best picture mode. This is significantly lower than competitors like the Hisense UR9, TCL QM8L, LG C6, and Samsung QN90. However, I do want to be clear this is no fault of the TV — Sony has a long standard of prioritizing accuracy and a true-to-Hollywood look over pure brightness in its Professional mode. Targeting 100 nits of brightness isn’t a bug to Sony. It’s a feature.</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="FJtHHg5fMzYwmFxN5ZgTkf" name="Sony-Bravia-7-II--7" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/FJtHHg5fMzYwmFxN5ZgTkf-1920-80.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>That said, your mileage will vary on how much you enjoy Professional mode. I watched a nature video of Norway on YouTube and the whites in the water lacked intensity and weren’t as bright as I hoped. Playing around with the various options in Sony’s new My Cinema presets in the settings menu optimized the SDR brightness to better suit my eyes. </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="5tKzi4PzJEW3rrfudB7Sof" name="Sony-Bravia-7-II--11" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/5tKzi4PzJEW3rrfudB7Sof-1920-80.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>Additional presets can optimize content from Netflix or Amazon Prime, but I found that switching to the standard Cinema Mode improved brightness better, pushing peaks closer to 750 nits. To be fair, Sony gives you full control and explains the various picture modes out of the box during setup, so it’s easy to change things around if you aren’t happy with the out-of-the-box settings. </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="2CEFUgzKPZbC5uTyhj6Pxf" name="Sony-Bravia-7-II--12" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/2CEFUgzKPZbC5uTyhj6Pxf-1920-80.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>So where does it falter? We measured a Delta-E of 4.79, which suggests the Bravia 7 II isn’t the strongest performer in color accuracy compared to the LG C6 or Samsung QN90F. (We reached out to Sony for clarification on why it’s higher than average and are still awaiting a response.)</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="cNX7JsZKn9GX2RFV32QFgf" name="Sony-Bravia-7-II--8" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/cNX7JsZKn9GX2RFV32QFgf-1920-80.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>Lab numbers don't always account for what the human eye can perceive through Sony's image processing. This means that sometimes a TV can still look good; personally, I felt that the TV delivered natural, balanced tones that you actually look for in real world-viewing. In SDR content like a NYC subway walkthrough on YouTube, faces still appeared lifelike. </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="pQs9Up5ogqoDJ4PxkY2gqf" name="Sony-Bravia-7-II--4" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/pQs9Up5ogqoDJ4PxkY2gqf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Bravia 7 II did not give the faces a cooler, washed-out look many other TVs in its class might produce. Similarly, in “Blade Runner 2049” on Blu-ray, the Las Vegas sequence kept true to the film’s orange hue without oversaturating natural skin tones.</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="R89Vu94UVrj8UNKrhMjRkf" name="Sony-Bravia-7-II--3" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/R89Vu94UVrj8UNKrhMjRkf-1920-80.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>Side viewing angles on this TV were impressive across all the content I watched, thanks to Sony’s X-wide Angle Pro technology.  After moving my seat to both sides of the TV and rewatching the same scenes I mentioned earlier from “Blade Runner 2049,” “The Batman,” and “Tom Clancy’s Jack Ryan Ghost War,” picture quality remained consistently strong. I didn’t notice any bleed or loss in color or brightness. This is an area where many non-OLED displays typically struggle, but the Bravia 7 II held up well.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-gaming"><span>Sony Bravia 7 II review: Gaming</span></h3><p>Gaming performance on the Bravia 7 II is a big jump from the Bravia 7: We measured input lag at 10.4ms, a huge step up from the 17.1ms from the Bravia 7 — and more in line with what we’ve seen with other 2026 TVs.</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="aCoW3tX66wkkMcAoRkCFuf" name="Sony-Bravia-7-II--2" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/aCoW3tX66wkkMcAoRkCFuf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I played <a href="https://www.tomsguide.com/gaming/lego-batman-legacy-of-the-dark-knight-review"><u><em>Lego Batman: Legacy of the Dark Knight</em></u></a> on the PlayStation 5 and had no issues.<em> </em>Controls felt tight and responsive during fast-paced fights where Batman and Robin took on street-level crime. There was almost no noticeable delay between my inputs and what I saw on the screen.</p><p>Since this is a Sony TV, there are added benefits when you pair it with a PS5: <a href="https://www.tomsguide.com/tvs/this-is-the-one-setting-on-your-hdr-tv-most-people-dont-know-about-heres-how-to-tweak-it"><u>Auto HDR Tone Mapping</u></a> lets you skip the HDR optimization you usually have to go through in most games.</p><p>But just be careful about which port you choose. If you connect to either HDMI 2.1 port on the TV, the Bravia 7 II will give you that sweet and smooth 120Hz refresh rate, support for variable refresh rate (VRR), and auto low-latency mode (ALLM). Pairing it with one of the two aforementioned HDMI 2.0 ports could result in the loss of some of this functionality.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-sound"><span>Sony Bravia 7 II review: Sound</span></h3><p>The built-in speakers on the Bravia 7 II are surprisingly pleasing. With a side-firing design, all the speakers are at ear level across the screen. This means dialogue and effects project clearly into the room rather than feeling trapped on the TV’s stand. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="Joo4esCdrVbA4vwkyUFJdf" name="Sony-Bravia-7-II--5" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/Joo4esCdrVbA4vwkyUFJdf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>In action content like “The Batman” and “Tom Clancy’s Jack Ryan Ghost War,” voices in films and shows were easy to follow even during more chaotic scenes. Rain, gunfire, and the orchestral score never broke the immersion that I experienced.</p><p>Interestingly, a step during the TVs setup process allows you to optimize the acoustics for viewing. Sit in front of the TV with the remote, and the TV will measure the sound levels in the room. This feature is great if you want to get the most out of the TV’s sound.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-smart-tv-and-remote"><span>Sony Bravia 7 II review: Smart TV and remote</span></h3><p>The remote isn’t the only part of the TV with a built-in microphone — there’s one built right into the TV. I’ve always found voice remotes awkward, having to hold the whole remote to my face just to speak to the TV, so this always-ready mic made searching and getting recommendations feel more natural and hands-free. And don’t worry, it can be quickly muted using a physical slider on the bottom left of the TV for added privacy. </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="uK85nXoQv5DbVvYoRgU2kf" name="Sony-Bravia-7-II--13" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/uK85nXoQv5DbVvYoRgU2kf-1920-80.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>Tying the whole system together is the Google TV smart platform whose interface remains clean and easy to navigate. No matter what I asked it, Google’s Gemini integration helped surface content across all my streaming apps in the more conversational, “ask a friend” style that I wish Siri on my Apple TV at home could do a better job with. It’s one of the <a href="https://www.tomsguide.com/entertainment/streaming-devices/best-streaming-devices"><u>best streaming TV</u></a> platforms for a reason.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/wMARhWBkzrhH7zTdyGHitf-1920-80.jpg" alt="Sony Bravia 7 II True RGB TV remote" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/YKwSZVsVxiAWr76uNkqLjf-1920-80.jpg" alt="Sony Bravia 7 II True RGB TV power button" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>The remote design is largely unchanged from last year, still with its familiar plastic finish and blue-speckled detailing. Aside from swapping the arrangement of a few buttons, Sony has added a new favorites button that lets you quickly jump to a preferred streaming app and there’s also a handy remote-finder feature should you lose it. (Pressing the power button on the TV can trigger a buzzer on the remote if it gets lost.) This is a small — but genuinely useful — touch for those moments where your remote might get lost in the sofa.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-verdict"><span>Sony Bravia 7 II review: Verdict</span></h3><p>Though it sits below the Bravia 9 II, the Sony Bravia 7 II still has solid HDR performance. It brings bright highlights, deep shadows, and impressively clean blacks with virtually no blooming thanks to its standout “True RGB” backlight. It also has a few upgrades from its predecessor — like the stand — that make it worth upgrading to if you own the original Bravia 7.</p><p>Its conservative SDR brightness can look dim in everyday viewing, however, and its higher-than-average pricing also pushes it close to more color-accurate OLED territory. Unless it’s Black Friday or Cyber Monday, you shouldn’t expect to see major discounts on it, either. </p><p>If the so-called “Sony Tax” is too steep for you, you might want to consider the often-on-sale LG C6 OLED or even last year’s C5 OLED, Tom’s Guide’s TV of the Year. </p><p>If you’re unfazed by the sticker price, the Bravia 7 II is a stand-out TV that I would recommend to folks who want an early look at an RGB TV’s benefits without shelling out for the Bravia 9 II.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-7-ii-true-rgb-tv-review</link>
                                                                            <description>
                            <![CDATA[ The Sony Bravia 7 II delivers impressive HDR brightness alongside shockingly good off-axis viewing. But the pricing makes it hard to win over anyone who might also be considering an OLED TV. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">3TtzzLAfbYYKzGqYkoR4Xi</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/asA46gXWXNBUjRSoTozknf-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 17 Jun 2026 17:34:08 +0000</pubDate>                                                                                                                                <updated>Thu, 18 Jun 2026 02:02:54 +0000</updated>
                                                                                                                                            <category><![CDATA[OLED TVs]]></category>
                                                    <category><![CDATA[TVs]]></category>
                                                                                                                    <dc:creator><![CDATA[ Arif Bacchus ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/45MeDeb7Xn988jfZfHW7UG-320-70.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/asA46gXWXNBUjRSoTozknf-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sony Bravia 7 II True RGB TV on a desk]]></media:description>                                                            <media:text><![CDATA[Sony Bravia 7 II True RGB TV on a desk]]></media:text>
                                <media:title type="plain"><![CDATA[Sony Bravia 7 II True RGB TV on a desk]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/asA46gXWXNBUjRSoTozknf-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>After six months of lab testing the year's newest TVs, it has become clear that the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html"><u>best TVs of 2026</u></a> are pushing brightness and color accuracy to new heights. Today’s flagship TVs from Hisense and TCL deliver good performance, thanks in large part to a backlight technology called Mini-RGB that’s changing the game for picture quality. </p><p>Yet Sony isn't sitting on the sidelines. Its new <a href="https://www.tomsguide.com/tvs/i-saw-sonys-true-rgb-up-close-and-the-downfall-of-oled-tvs-might-come-sooner-than-expected"><u>"True RGB" technology</u></a> also promises independently controlled red, green, and blue LEDs under the display panel but, according to Sony, offers “purer color expression, improved light steering, and ultra-wide viewing angles.” The best part? You don’t need to empty your pockets to get a taste. Despite it being used in the flagship <a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs"><u>Bravia 9 II</u></a> sibling, you can get it on the Bravia 7 II at a lower price.</p><p>But does Sony’s new tech actually live up to the hype? After putting the Sony Bravia 7 II through our rigorous lab testing workflow and spending two days watching real-world content, the results didn’t blow my expectations out of the water, but I’m still very impressed.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-pricing-and-availability"><span>Sony Bravia 7 II review: Pricing and availability</span></h3><p>The Bravia 7 II is  $300 more than the starting price of the 65-inch <a href="https://www.tomsguide.com/tvs/sony-bravia-7-tv-review"><u>Sony Bravia 7</u></a> we reviewed in 2025. Pricing for the Sony Bravia 7 II starts at $1,600 / AU$2,699 for the<a href="https://electronics.sony.com/tv-video/televisions/all-tvs/p/k50xr70m2"> <u>50-inch variant</u></a>, and goes as high as $9,000 / AU$12,999 for the 98-inch model. </p><p>MSRPs / RRPs for the whole range are listed below: </p><ul><li>50-inch: $1,599 / AU$2,699</li><li>55-inch: $2,099 / N/A</li><li>65-inch: $2,599 / AU$3,999</li><li>75-inch: $3,099 / AU$5,499</li><li>85-inch: $3,999 / AU$7,499</li><li>98-inch: $8,999 / AU$12,999</li></ul><p>The 65-inch review unit we have comes in at $2,600 / AU$3,999. Sony’s flagship Bravia 9 II costs $3,600 / AU$5,999, so if you buy the Bravia 7 II, you can save an extra $1,000 / AU$2,000. Stepping down means sacrificing a handful of dimming zones, the anti-glare features, and slightly more premium audio features — but these compromises aren’t so bad given the price difference.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="FAg72YbFiogDCcq3a3DLmf" name="Sony-Bravia-7-II--17" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/FAg72YbFiogDCcq3a3DLmf-1920-80.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>That said, the 7 II is venturing close to OLED pricing territory. The <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled-review"><u>LG C6 OLED </u></a>currently sits at $2,700, while the <a href="https://www.tomsguide.com/tvs/4k-tvs/lg-b6-oled-tv-review"><u>LG B6</u></a> costs roughly $2,000. TCL’s RGB offering, the <a href="https://www.tomsguide.com/tvs/qled-tvs/tcl-sqd-mini-led-qm8l-75-inch-tv-review"><u>QM8L</u></a>, has a price tag of $3,000 for the 75-inch model while the <a href="https://www.tomsguide.com/tvs/qled-tvs/hisense-ur9-rgb-mini-led-tv-review"><u>Hisense UR9</u></a> costs $2,000 for a 65-inch screen after the nearly unheard of $1,500 discount it received immediately after launch.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-design-and-ports"><span>Sony Bravia 7 II review: Design and ports</span></h3><p>The Sony Bravia 7 II immediately felt different to me the moment I took it out of the box, largely due to its redesigned center-facing stand. As part of the setup, you slot a clear glass-like sheet  into the front of the stand. Sony calls this the “Mirage Stand.” This helps hide the cables that might be behind the TV. It also helps light and color pass through. </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="K3JdjXdoJhZ6NFzr74k2jf" name="Sony-Bravia-7-II--22" alt="Sony Bravia 7 II True RGB TV pedestal" src="https://cdn.mos.cms.futurecdn.net/K3JdjXdoJhZ6NFzr74k2jf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The assembly was easy, involving building out both sides of the base, and then simply sliding the entire TV into place and fastening it with screws. Compared to my experience with setting up other TVs, the setup feels remarkably seamless. I wish other manufacturers would embrace this approach. The trade-off is that, on the sides, the Bravia 7 II is still slightly thick, similar to other RGB TVs in this class.</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="udLZDxWgK3osJPjsSfTrhf" name="Sony-Bravia-7-II--20" alt="Sony Bravia 7 II True RGB TV screen thickness" src="https://cdn.mos.cms.futurecdn.net/udLZDxWgK3osJPjsSfTrhf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The other issue besides its thickness is that Sony skimped out on ports: On the left side of the TV, there are four HDMI ports, just like last year’s model, but two of them are the older HDMI 2.0b standard. (HDMI 1 and 2 are HDMI 2.0b, while HDMI 3 and 4 are HDMI 2.1.) </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="VSxBS9GT5KHaZdTGTPUanf" name="Sony-Bravia-7-II--24" alt="Sony Bravia 7 II True RGB TV ports" src="https://cdn.mos.cms.futurecdn.net/VSxBS9GT5KHaZdTGTPUanf-1920-80.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>Those HDMI ports are all in addition to the two USB ports, optical audio out, S-Center speaker in, Ethernet, RS-232C remote, IR in, and a spot for a coaxial cable. </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="HdgvWQo6iku5Je3wgGGvnf" name="Sony-Bravia-7-II--18" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/HdgvWQo6iku5Je3wgGGvnf-1920-80.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>Finally, around the back, the non-detachable power cable is on the right rear. In that same area, Sony also gives you a spot to clip that cable to better help manage wires. The TV can be wall-mounted using the VESA holes on the back panel, should you prefer that.</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="wdvUHJiMrCkBhamxYSeuff" name="Sony-Bravia-7-II--21" alt="Sony Bravia 7 II True RGB TV pedestal" src="https://cdn.mos.cms.futurecdn.net/wdvUHJiMrCkBhamxYSeuff-1920-80.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>My biggest gripe with the 7 II’s design is that, for this price, it should have HDMI 2.1 ports standard. If you own multiple game consoles like I do, you’ll only be able to fully enjoy them at 120Hz on two of this TV's four ports. It’s not a deal breaker, but it is a small disappointment.</p><h3 class="article-body__section" id="section-how-we-test"><span>How we test</span></h3><p>Our TV review process at Tom's Guide for TVs is rigorous, but standardized. In our New York City test lab, my lab assistant and I test every TV across SDR and HDR benchmarks using the industry standard 10% window.</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="W3sBn7HXPCUfJ7jVg7F6pf" name="Sony-Bravia-7-II--16" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/W3sBn7HXPCUfJ7jVg7F6pf-1920-80.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>To ensure we are seeing the director's true vision, we switch the TV over to Filmmaker Mode which is typically the most color-accurate setting available. We also disable any AI features, Eco Mode, and any intrusive ambient light sensors that could impact readings.</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="MZnR9EJAfysrvkb29vAwmf" name="Sony-Bravia-7-II--15" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/MZnR9EJAfysrvkb29vAwmf-1920-80.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>We use the Jeti spectraval 1501-HiRes spectroradiometer to profile our Klein K10-A colorimeter. Once profiled, this high-end hardware works in tandem with a Murideo 8K-SIX-G Metal pattern generator and a custom workflow in <a href="https://www.portrait.com/calman-home/"><u>Portrait Displays Calman Ultimate software</u></a> to map out the TV’s performance. </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="gYtxBEtsg7bmJkuVDqaejf" name="Sony-Bravia-7-II--14" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/gYtxBEtsg7bmJkuVDqaejf-1920-80.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>For gaming performance, we use a Leo Bodnar 4K Video Signal Lag Tester to measure exactly how responsive the TV really is. Once technical lab testing is done, we put the TV through subjective testing using a curated selection of films and shows. For more on this process, read our “<a href="https://www.tomsguide.com/us/how-we-test-tvs,review-2478.html"><u>How we test TVs</u></a>” guide.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-performance-and-test-results"><span>Sony Bravia 7 II review: Performance and test results</span></h3><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Sony Bravia 7 II</strong></p></td><td  ><p><strong>Sony Bravia 7</strong></p></td><td  ><p><strong>Hisense UR9</strong></p></td><td  ><p><strong>TCL QM8L</strong></p></td><td  ><p><strong>LG C6</strong></p></td><td  ><p><strong>Samsung QN90F</strong></p></td></tr><tr><td class="firstcol " ><p><strong>SDR Brightness (10%, in nits)</strong></p></td><td  ><p>100</p></td><td  ><p>101</p></td><td  ><p>2,486</p></td><td  ><p>229</p></td><td  ><p>355</p></td><td  ><p>228</p></td></tr><tr><td class="firstcol " ><p><strong>Delta-E (lower is better)</strong></p></td><td  ><p>4.79</p></td><td  ><p>1.58</p></td><td  ><p>2.72</p></td><td  ><p>3.74</p></td><td  ><p>1.53</p></td><td  ><p>1.83</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 709 Gamut Coverage</strong></p></td><td  ><p>99.94%</p></td><td  ><p>99.62%</p></td><td  ><p>99.93%</p></td><td  ><p>99.55%</p></td><td  ><p>97.8%</p></td><td  ><p>99.15%</p></td></tr><tr><td class="firstcol " ><p><strong>HDR Brightness (10%, in nits)</strong></p></td><td  ><p>2,077</p></td><td  ><p>1,355</p></td><td  ><p>3,327</p></td><td  ><p>3,719</p></td><td  ><p>1,355</p></td><td  ><p>2,109</p></td></tr><tr><td class="firstcol " ><p><strong>UHDA-P3 Gamut Coverage</strong></p></td><td  ><p>97.73%</p></td><td  ><p>97.09%</p></td><td  ><p>99.83%</p></td><td  ><p>97.97%</p></td><td  ><p>99.45%</p></td><td  ><p>99.32%</p></td></tr><tr><td class="firstcol " ><p><strong>Rec. 2020 Gamut Coverage</strong></p></td><td  ><p>87.17%</p></td><td  ><p>78.61%</p></td><td  ><p>93.03%</p></td><td  ><p>90.34%</p></td><td  ><p>76.18%</p></td><td  ><p>77.44%</p></td></tr><tr><td class="firstcol " ><p><strong>Input latency (in milliseconds)</strong></p></td><td  ><p>10.4</p></td><td  ><p>17.1</p></td><td  ><p>12</p></td><td  ><p>9.5</p></td><td  ><p>12.9/9.1</p></td><td  ><p>9.5</p><p><br></p></td></tr></tbody></table></div><p>HDR performance is where the Bravia 7 II comes into its own league, with the peak brightness reaching 2,077 nits with our 10% test window. This is far ahead of what we've seen on OLED TVs like LG C6 and nearly double that of the original Bravia 7 model. However, it’s overshadowed by the Hisense UR9 and TCL QM8L.</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="TffrVPdDz5iRyeeE84Hggf" name="Sony-Bravia-7-II--6" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/TffrVPdDz5iRyeeE84Hggf-1920-80.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>When watching scenes in HDR  from <em>“The Batman”</em> on our lab’s Blu-ray player, I immediately noticed the highlights from headlights and explosions against the shadows of the night. Yet despite all of this, the black in Batman’s suit and the Batmobile remained true. </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="JeLhmPPKVvnD9ew2Lbrojf" name="Sony-Bravia-7-II--10" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/JeLhmPPKVvnD9ew2Lbrojf-1920-80.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>Streaming <em>“Tom Clancy’s Jack Ryan Ghost War”</em> on Amazon Prime, my initial impressions were corroborated: The TV rendered some really deep blacks and precisely managed highlights, ensuring that the riverside lamps in the introductory sequence showed absolutely no blooming against a black dark sky. This carried over into the film's climactic final battle, where even in dark night scenes, I could not spot any light bleed around the vibrant orange muzzle flashes or the crisp white LED signs on a building set against the dark night.</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="2L4yXSZEu2MuP994fuqunf" name="Sony-Bravia-7-II--9" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/2L4yXSZEu2MuP994fuqunf-1920-80.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>For SDR performance, Bravia TVs come with conservative brightness settings out of the box. As a result, SDR brightness measured just 100 nits in our lab testing for the TV’s best picture mode. This is significantly lower than competitors like the Hisense UR9, TCL QM8L, LG C6, and Samsung QN90. However, I do want to be clear this is no fault of the TV — Sony has a long standard of prioritizing accuracy and a true-to-Hollywood look over pure brightness in its Professional mode. Targeting 100 nits of brightness isn’t a bug to Sony. It’s a feature.</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="FJtHHg5fMzYwmFxN5ZgTkf" name="Sony-Bravia-7-II--7" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/FJtHHg5fMzYwmFxN5ZgTkf-1920-80.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>That said, your mileage will vary on how much you enjoy Professional mode. I watched a nature video of Norway on YouTube and the whites in the water lacked intensity and weren’t as bright as I hoped. Playing around with the various options in Sony’s new My Cinema presets in the settings menu optimized the SDR brightness to better suit my eyes. </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="5tKzi4PzJEW3rrfudB7Sof" name="Sony-Bravia-7-II--11" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/5tKzi4PzJEW3rrfudB7Sof-1920-80.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>Additional presets can optimize content from Netflix or Amazon Prime, but I found that switching to the standard Cinema Mode improved brightness better, pushing peaks closer to 750 nits. To be fair, Sony gives you full control and explains the various picture modes out of the box during setup, so it’s easy to change things around if you aren’t happy with the out-of-the-box settings. </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="2CEFUgzKPZbC5uTyhj6Pxf" name="Sony-Bravia-7-II--12" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/2CEFUgzKPZbC5uTyhj6Pxf-1920-80.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>So where does it falter? We measured a Delta-E of 4.79, which suggests the Bravia 7 II isn’t the strongest performer in color accuracy compared to the LG C6 or Samsung QN90F. (We reached out to Sony for clarification on why it’s higher than average and are still awaiting a response.)</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="cNX7JsZKn9GX2RFV32QFgf" name="Sony-Bravia-7-II--8" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/cNX7JsZKn9GX2RFV32QFgf-1920-80.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>Lab numbers don't always account for what the human eye can perceive through Sony's image processing. This means that sometimes a TV can still look good; personally, I felt that the TV delivered natural, balanced tones that you actually look for in real world-viewing. In SDR content like a NYC subway walkthrough on YouTube, faces still appeared lifelike. </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="pQs9Up5ogqoDJ4PxkY2gqf" name="Sony-Bravia-7-II--4" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/pQs9Up5ogqoDJ4PxkY2gqf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Bravia 7 II did not give the faces a cooler, washed-out look many other TVs in its class might produce. Similarly, in “Blade Runner 2049” on Blu-ray, the Las Vegas sequence kept true to the film’s orange hue without oversaturating natural skin tones.</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="R89Vu94UVrj8UNKrhMjRkf" name="Sony-Bravia-7-II--3" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/R89Vu94UVrj8UNKrhMjRkf-1920-80.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>Side viewing angles on this TV were impressive across all the content I watched, thanks to Sony’s X-wide Angle Pro technology.  After moving my seat to both sides of the TV and rewatching the same scenes I mentioned earlier from “Blade Runner 2049,” “The Batman,” and “Tom Clancy’s Jack Ryan Ghost War,” picture quality remained consistently strong. I didn’t notice any bleed or loss in color or brightness. This is an area where many non-OLED displays typically struggle, but the Bravia 7 II held up well.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-gaming"><span>Sony Bravia 7 II review: Gaming</span></h3><p>Gaming performance on the Bravia 7 II is a big jump from the Bravia 7: We measured input lag at 10.4ms, a huge step up from the 17.1ms from the Bravia 7 — and more in line with what we’ve seen with other 2026 TVs.</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="aCoW3tX66wkkMcAoRkCFuf" name="Sony-Bravia-7-II--2" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/aCoW3tX66wkkMcAoRkCFuf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I played <a href="https://www.tomsguide.com/gaming/lego-batman-legacy-of-the-dark-knight-review"><u><em>Lego Batman: Legacy of the Dark Knight</em></u></a> on the PlayStation 5 and had no issues.<em> </em>Controls felt tight and responsive during fast-paced fights where Batman and Robin took on street-level crime. There was almost no noticeable delay between my inputs and what I saw on the screen.</p><p>Since this is a Sony TV, there are added benefits when you pair it with a PS5: <a href="https://www.tomsguide.com/tvs/this-is-the-one-setting-on-your-hdr-tv-most-people-dont-know-about-heres-how-to-tweak-it"><u>Auto HDR Tone Mapping</u></a> lets you skip the HDR optimization you usually have to go through in most games.</p><p>But just be careful about which port you choose. If you connect to either HDMI 2.1 port on the TV, the Bravia 7 II will give you that sweet and smooth 120Hz refresh rate, support for variable refresh rate (VRR), and auto low-latency mode (ALLM). Pairing it with one of the two aforementioned HDMI 2.0 ports could result in the loss of some of this functionality.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-sound"><span>Sony Bravia 7 II review: Sound</span></h3><p>The built-in speakers on the Bravia 7 II are surprisingly pleasing. With a side-firing design, all the speakers are at ear level across the screen. This means dialogue and effects project clearly into the room rather than feeling trapped on the TV’s stand. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="Joo4esCdrVbA4vwkyUFJdf" name="Sony-Bravia-7-II--5" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/Joo4esCdrVbA4vwkyUFJdf-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>In action content like “The Batman” and “Tom Clancy’s Jack Ryan Ghost War,” voices in films and shows were easy to follow even during more chaotic scenes. Rain, gunfire, and the orchestral score never broke the immersion that I experienced.</p><p>Interestingly, a step during the TVs setup process allows you to optimize the acoustics for viewing. Sit in front of the TV with the remote, and the TV will measure the sound levels in the room. This feature is great if you want to get the most out of the TV’s sound.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-smart-tv-and-remote"><span>Sony Bravia 7 II review: Smart TV and remote</span></h3><p>The remote isn’t the only part of the TV with a built-in microphone — there’s one built right into the TV. I’ve always found voice remotes awkward, having to hold the whole remote to my face just to speak to the TV, so this always-ready mic made searching and getting recommendations feel more natural and hands-free. And don’t worry, it can be quickly muted using a physical slider on the bottom left of the TV for added privacy. </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="uK85nXoQv5DbVvYoRgU2kf" name="Sony-Bravia-7-II--13" alt="Sony Bravia 7 II True RGB TV on a desk" src="https://cdn.mos.cms.futurecdn.net/uK85nXoQv5DbVvYoRgU2kf-1920-80.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>Tying the whole system together is the Google TV smart platform whose interface remains clean and easy to navigate. No matter what I asked it, Google’s Gemini integration helped surface content across all my streaming apps in the more conversational, “ask a friend” style that I wish Siri on my Apple TV at home could do a better job with. It’s one of the <a href="https://www.tomsguide.com/entertainment/streaming-devices/best-streaming-devices"><u>best streaming TV</u></a> platforms for a reason.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/wMARhWBkzrhH7zTdyGHitf-1920-80.jpg" alt="Sony Bravia 7 II True RGB TV remote" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/YKwSZVsVxiAWr76uNkqLjf-1920-80.jpg" alt="Sony Bravia 7 II True RGB TV power button" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>The remote design is largely unchanged from last year, still with its familiar plastic finish and blue-speckled detailing. Aside from swapping the arrangement of a few buttons, Sony has added a new favorites button that lets you quickly jump to a preferred streaming app and there’s also a handy remote-finder feature should you lose it. (Pressing the power button on the TV can trigger a buzzer on the remote if it gets lost.) This is a small — but genuinely useful — touch for those moments where your remote might get lost in the sofa.</p><h3 class="article-body__section" id="section-sony-bravia-7-ii-review-verdict"><span>Sony Bravia 7 II review: Verdict</span></h3><p>Though it sits below the Bravia 9 II, the Sony Bravia 7 II still has solid HDR performance. It brings bright highlights, deep shadows, and impressively clean blacks with virtually no blooming thanks to its standout “True RGB” backlight. It also has a few upgrades from its predecessor — like the stand — that make it worth upgrading to if you own the original Bravia 7.</p><p>Its conservative SDR brightness can look dim in everyday viewing, however, and its higher-than-average pricing also pushes it close to more color-accurate OLED territory. Unless it’s Black Friday or Cyber Monday, you shouldn’t expect to see major discounts on it, either. </p><p>If the so-called “Sony Tax” is too steep for you, you might want to consider the often-on-sale LG C6 OLED or even last year’s C5 OLED, Tom’s Guide’s TV of the Year. </p><p>If you’re unfazed by the sticker price, the Bravia 7 II is a stand-out TV that I would recommend to folks who want an early look at an RGB TV’s benefits without shelling out for the Bravia 9 II.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Is this rumored Sony OLED TV the LG C6 rival we've been waiting for? ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Across the web, rumors are swirling about a brand-new Sony OLED TV dubbed the Bravia 6. It shouldn't come as a shock, considering the lack of an entry-level OLED in Sony's current Bravia lineup, and the Bravia 6 could very well be the <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled-review">LG C6</a> rival we've been waiting for.</p><p>Sony makes some of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> on the market — and this could be its most exciting model yet. Here's what we know about it so far.</p><h2 id="sony-bravia-6-everything-we-know-so-far">Sony Bravia 6: everything we know so far</h2><p>I first came across Bravia 6 speculation via a report at <a href="https://www.flatpanelshd.com/news.php?subaction=showfull&id=1780646829" target="_blank" rel="nofollow">FlatpanelsHD</a>, who in turn cited <a href="https://x.com/High_Def_News" target="_blank" rel="nofollow">High Def News</a> on X (formerly Twitter). That post came as a result of a post on the <a href="https://www.avsforum.com/threads/sony-2026-rgb-mini-led-technology-tv-discussion.3322029/page-127?post_id=64643460#post-64643460" target="_blank" rel="nofollow">AVS Forum</a> message board.</p><p>Despite this game of telephone, the facts on the ground remain rather simple: Sony has registered the Bravia 6 namesake across several online databases. </p><p>On Sony's own website, Bravia 6 model numbers (seen below) are listed as being compatible with select wall-mounting brackets. </p><div ><table><thead><tr><th class="firstcol " ><p>Model number</p></th><th  ><p>Size</p></th></tr></thead><tbody><tr><td class="firstcol " ><p>K-48A60</p></td><td  ><p>48 inches</p></td></tr><tr><td class="firstcol " ><p>K-55A60</p></td><td  ><p>55 inches</p></td></tr><tr><td class="firstcol " ><p>K-65A60</p></td><td  ><p>65 inches</p></td></tr><tr><td class="firstcol " ><p>K-77A60</p></td><td  ><p>77 inches</p></td></tr><tr><td class="firstcol " ><p>K-83A60</p></td><td  ><p>83 inches</p></td></tr></tbody></table></div><p>Interestingly, the Bravia 6 appears as though it'll be available in a similar size range as two of its rivals' most popular OLED TVs (the LG C6 and the Samsung S90H). This particular range of sizes would also indicate that the Bravia 6 will, at the very least, feature <em>some</em> WOLED-based panels, since we've yet to QD-OLED panels (manufactured by Samsung Display) in 48- and 83-inch sizes.</p><p>However, it's unclear whether <em>all</em> of the sizes in the Bravia 6 series would feature LG Display's standard WOLED panels, or just some of them.</p><div  class="fancy-box"><div class="fancy_box-title">Want to learn more?</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="G2nr9XbLfew6QLc7an3sK5" name="LG-G5-OLED" caption="" alt="Lifestyle image showing a wall-mounted LG G5 OLED TV a living room environment" src="https://cdn.mos.cms.futurecdn.net/G2nr9XbLfew6QLc7an3sK5-1920-80.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: LG)</span></figcaption></figure><p class="fancy-box__body-text"><strong>Check out my breakdown of the </strong><a data-analytics-id="inline-link" href="https://www.tomsguide.com/tvs/oled-tvs/qd-oled-vs-woled-whats-the-difference-in-2025"><strong>difference between WOLED and QD-OLED</strong></a><strong>.</strong></p></div></div><p>Bear in mind that LG Display manufacturers different subtypes of WOLED panels, too. RGB Tandem WOLED panels are engineered for better brightness and color, and therefore tend to be attached to higher-end OLED TVs.</p><p>And then there's <a href="https://www.tomsguide.com/tvs/oled-tvs/some-oled-tvs-are-getting-brighter-and-cheaper-in-2026-heres-what-you-need-to-know-before-shopping">OLED SE</a>, a new type of WOLED panel from LG Display that has already made its way into a more-affordable class of OLED TVs in 2027. The Bravia 6 could conceivably tap this OLED type, too.</p><p>Knowing what type of OLED panel the Bravia 6 uses could tell us more about its possible price range. Alternatively, knowing the Bravia 6's price range could reveal more about its panel tech. With neither piece in place, this is all very much up in the air.</p><div class="product"><a data-dimension112="bcd02b54-5a06-44a0-b347-ecef2eb2f244" data-action="Deal Block" data-label="65" C6 4K OLED TV" data-dimension48="65" C6 4K OLED TV" data-dimension25="$2699" href="https://www.bestbuy.com/product/lg-65-class-c6-series-oled-evo-ai-4k-smart-webos-tv-2026/JJ8VPZKZ8H" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="yZJJv6Rpskz5oXrVozrW7o" name="LG-C6-OLED-TV-16-by-9-deal-block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yZJJv6Rpskz5oXrVozrW7o-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/lg-65-class-c6-series-oled-evo-ai-4k-smart-webos-tv-2026/JJ8VPZKZ8H" target="_blank" rel="nofollow" data-dimension112="bcd02b54-5a06-44a0-b347-ecef2eb2f244" data-action="Deal Block" data-label='65" C6 4K OLED TV' data-dimension48='65" C6 4K OLED TV' data-dimension25="$2699">65" C6 4K OLED TV: $2699</a></strong><br>The newest version of LG's celebrated C Series OLED is another winner, thanks to a brighter picture than its predecessor and a similar set of gaming- and streaming-related features. If you can't wait to find out whether or not the Sony Bravia 6 actually exists, the C6 is a fine choice. It's one of the best mid-range OLEDs of the year.<a class="view-deal button" href="https://www.bestbuy.com/product/lg-65-class-c6-series-oled-evo-ai-4k-smart-webos-tv-2026/JJ8VPZKZ8H" target="_blank" rel="nofollow" data-dimension112="bcd02b54-5a06-44a0-b347-ecef2eb2f244" data-action="Deal Block" data-label="65" C6 4K OLED TV" data-dimension48="65" C6 4K OLED TV" data-dimension25="$2699">View Deal</a></p></div><h2 id="will-the-bravia-6-go-head-to-head-with-the-lg-c6">Will the Bravia 6 go head to head with the LG C6?</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="cSTGNGmq6xfMtjG9k7aMVi" name="LG-C6--3" alt="LG C6 OLED TV shown on a wood table" src="https://cdn.mos.cms.futurecdn.net/cSTGNGmq6xfMtjG9k7aMVi-1920-80.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>This is the million-dollar question. </p><p>While LG and Samsung have offered annual, similarly structured OLED lineups with entry-level, mid-range and high-end options, Sony's Bravia OLED series have only a limited number of options.</p><p>For example, the <a href="https://www.tomsguide.com/tvs/sony-bravia-8-oled-tv">Bravia 8 OLED</a> that launched in 2024, spent much of its life living alongside the higher-end, quantum dot-equipped <a href="https://www.tomsguide.com/reviews/sony-bravia-xr-a95l-qd-oled-tv">Sony A95L OLED</a>.</p><p>In 2025, the Bravia 8's successor — the <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-8-ii-review">Bravia 8 II</a> — incorporated quantum dots, elevating it closer to the A95L's higher-end stratum. </p><div><blockquote><p>So far in 2026, Sony has yet to confirm the existence of a new OLED TV.</p></blockquote></div><p>But, so far in 2026, Sony has yet to confirm the existence of a new OLED TV, and currently, all three of these higher-end Sony-branded OLEDs are sitting on shelves simultaneously.</p><p>For the Bravia 6 to compete with mid-range OLEDs like the LG C6 and the Samsung S90H, it wouldn't necessarily need to be Sony's mid-range model <em>per se</em>; it would only need to hover around the same price point as its rivals. </p><p>Hopefully Sony isn't backing away from OLED anytime soon. It was worrying that the brand was content to leave its current OLED lineup on shelves for another calendar year — but, if real, the Bravia 6 would prove that Sony still sees a place for OLED alongside its True RGB TVs like the <a href="https://www.tomsguide.com/tvs/we-just-tested-the-bravia-7-ii-heres-why-i-think-it-could-be-the-sony-tv-to-buy-in-2026">Bravia 7 II</a> and <a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs">9 II</a>.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eJkrEW"></div>                            </div>                            <script src="https://kwizly.com/embed/eJkrEW.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/tvs/i-saw-sonys-true-rgb-up-close-and-the-downfall-of-oled-tvs-might-come-sooner-than-expected">I saw Sony's True RGB up close — and the downfall of OLED TVs might come sooner than expected</a></li><li><a href="https://www.tomsguide.com/tvs/tcl-and-sony-are-joining-forces-to-sell-more-tvs-but-is-that-bad-news-for-shoppers">TCL and Sony are joining forces to sell more TVs — but is that bad news for shoppers?</a></li><li><a href="https://www.tomsguide.com/tvs/we-just-tested-the-bravia-7-ii-heres-why-i-think-it-could-be-the-sony-tv-to-buy-in-2026">We just tested the Bravia 7 II — here’s why I think it could be the Sony TV to buy in 2026</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/oled-tvs/is-this-rumored-sony-oled-tv-the-lg-c6-rival-weve-been-waiting-for</link>
                                                                            <description>
                            <![CDATA[ Rumors are swirling about a new Sony OLED TV called the Bravia 6. Here's how it might stack up against competitors from LG and Samsung. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">RMnfJnGZoFfxmjWGETbPQ4</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/cpZYUSzNf4b7iZCWdWMyDm-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 10 Jun 2026 11:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[OLED TVs]]></category>
                                                    <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ michael.desjardin@futurenet.com (Michael Desjardin) ]]></author>                    <dc:creator><![CDATA[ Michael Desjardin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/esytxRqW9GNVZNVCf7BFyV-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael Desjardin is a Senior Editor for TVs at Tom&#039;s Guide. He&#039;s been testing and tinkering with TVs professionally for over a decade, previously for Reviewed and USA Today. Michael studied media production and screenwriting at Emerson College. He loves cooking, zoning out to ambient music, and getting way too invested in the Red Sox. He considers himself living proof that TV doesn&#039;t necessarily rot your brain.&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/cpZYUSzNf4b7iZCWdWMyDm-1920-80.jpg">
                                                            <media:credit><![CDATA[Sony]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sony Bravia 8 on stand in living room]]></media:description>                                                            <media:text><![CDATA[Sony Bravia 8 on stand in living room]]></media:text>
                                <media:title type="plain"><![CDATA[Sony Bravia 8 on stand in living room]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/cpZYUSzNf4b7iZCWdWMyDm-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Across the web, rumors are swirling about a brand-new Sony OLED TV dubbed the Bravia 6. It shouldn't come as a shock, considering the lack of an entry-level OLED in Sony's current Bravia lineup, and the Bravia 6 could very well be the <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled-review">LG C6</a> rival we've been waiting for.</p><p>Sony makes some of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> on the market — and this could be its most exciting model yet. Here's what we know about it so far.</p><h2 id="sony-bravia-6-everything-we-know-so-far">Sony Bravia 6: everything we know so far</h2><p>I first came across Bravia 6 speculation via a report at <a href="https://www.flatpanelshd.com/news.php?subaction=showfull&id=1780646829" target="_blank" rel="nofollow">FlatpanelsHD</a>, who in turn cited <a href="https://x.com/High_Def_News" target="_blank" rel="nofollow">High Def News</a> on X (formerly Twitter). That post came as a result of a post on the <a href="https://www.avsforum.com/threads/sony-2026-rgb-mini-led-technology-tv-discussion.3322029/page-127?post_id=64643460#post-64643460" target="_blank" rel="nofollow">AVS Forum</a> message board.</p><p>Despite this game of telephone, the facts on the ground remain rather simple: Sony has registered the Bravia 6 namesake across several online databases. </p><p>On Sony's own website, Bravia 6 model numbers (seen below) are listed as being compatible with select wall-mounting brackets. </p><div ><table><thead><tr><th class="firstcol " ><p>Model number</p></th><th  ><p>Size</p></th></tr></thead><tbody><tr><td class="firstcol " ><p>K-48A60</p></td><td  ><p>48 inches</p></td></tr><tr><td class="firstcol " ><p>K-55A60</p></td><td  ><p>55 inches</p></td></tr><tr><td class="firstcol " ><p>K-65A60</p></td><td  ><p>65 inches</p></td></tr><tr><td class="firstcol " ><p>K-77A60</p></td><td  ><p>77 inches</p></td></tr><tr><td class="firstcol " ><p>K-83A60</p></td><td  ><p>83 inches</p></td></tr></tbody></table></div><p>Interestingly, the Bravia 6 appears as though it'll be available in a similar size range as two of its rivals' most popular OLED TVs (the LG C6 and the Samsung S90H). This particular range of sizes would also indicate that the Bravia 6 will, at the very least, feature <em>some</em> WOLED-based panels, since we've yet to QD-OLED panels (manufactured by Samsung Display) in 48- and 83-inch sizes.</p><p>However, it's unclear whether <em>all</em> of the sizes in the Bravia 6 series would feature LG Display's standard WOLED panels, or just some of them.</p><div  class="fancy-box"><div class="fancy_box-title">Want to learn more?</div><div class="fancy_box_body"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' ><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="G2nr9XbLfew6QLc7an3sK5" name="LG-G5-OLED" caption="" alt="Lifestyle image showing a wall-mounted LG G5 OLED TV a living room environment" src="https://cdn.mos.cms.futurecdn.net/G2nr9XbLfew6QLc7an3sK5-1920-80.jpg" mos="" link="" align="" fullscreen="" width="" height="" attribution="" endorsement="" class="pinterest-pin-exclude"></p></div></div><figcaption itemprop="caption description" class=""><span class="credit" itemprop="copyrightHolder">(Image credit: LG)</span></figcaption></figure><p class="fancy-box__body-text"><strong>Check out my breakdown of the </strong><a data-analytics-id="inline-link" href="https://www.tomsguide.com/tvs/oled-tvs/qd-oled-vs-woled-whats-the-difference-in-2025"><strong>difference between WOLED and QD-OLED</strong></a><strong>.</strong></p></div></div><p>Bear in mind that LG Display manufacturers different subtypes of WOLED panels, too. RGB Tandem WOLED panels are engineered for better brightness and color, and therefore tend to be attached to higher-end OLED TVs.</p><p>And then there's <a href="https://www.tomsguide.com/tvs/oled-tvs/some-oled-tvs-are-getting-brighter-and-cheaper-in-2026-heres-what-you-need-to-know-before-shopping">OLED SE</a>, a new type of WOLED panel from LG Display that has already made its way into a more-affordable class of OLED TVs in 2027. The Bravia 6 could conceivably tap this OLED type, too.</p><p>Knowing what type of OLED panel the Bravia 6 uses could tell us more about its possible price range. Alternatively, knowing the Bravia 6's price range could reveal more about its panel tech. With neither piece in place, this is all very much up in the air.</p><div class="product"><a data-dimension112="bcd02b54-5a06-44a0-b347-ecef2eb2f244" data-action="Deal Block" data-label="65" C6 4K OLED TV" data-dimension48="65" C6 4K OLED TV" data-dimension25="$2699" href="https://www.bestbuy.com/product/lg-65-class-c6-series-oled-evo-ai-4k-smart-webos-tv-2026/JJ8VPZKZ8H" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="yZJJv6Rpskz5oXrVozrW7o" name="LG-C6-OLED-TV-16-by-9-deal-block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yZJJv6Rpskz5oXrVozrW7o-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/lg-65-class-c6-series-oled-evo-ai-4k-smart-webos-tv-2026/JJ8VPZKZ8H" target="_blank" rel="nofollow" data-dimension112="bcd02b54-5a06-44a0-b347-ecef2eb2f244" data-action="Deal Block" data-label='65" C6 4K OLED TV' data-dimension48='65" C6 4K OLED TV' data-dimension25="$2699">65" C6 4K OLED TV: $2699</a></strong><br>The newest version of LG's celebrated C Series OLED is another winner, thanks to a brighter picture than its predecessor and a similar set of gaming- and streaming-related features. If you can't wait to find out whether or not the Sony Bravia 6 actually exists, the C6 is a fine choice. It's one of the best mid-range OLEDs of the year.<a class="view-deal button" href="https://www.bestbuy.com/product/lg-65-class-c6-series-oled-evo-ai-4k-smart-webos-tv-2026/JJ8VPZKZ8H" target="_blank" rel="nofollow" data-dimension112="bcd02b54-5a06-44a0-b347-ecef2eb2f244" data-action="Deal Block" data-label="65" C6 4K OLED TV" data-dimension48="65" C6 4K OLED TV" data-dimension25="$2699">View Deal</a></p></div><h2 id="will-the-bravia-6-go-head-to-head-with-the-lg-c6">Will the Bravia 6 go head to head with the LG C6?</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="cSTGNGmq6xfMtjG9k7aMVi" name="LG-C6--3" alt="LG C6 OLED TV shown on a wood table" src="https://cdn.mos.cms.futurecdn.net/cSTGNGmq6xfMtjG9k7aMVi-1920-80.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>This is the million-dollar question. </p><p>While LG and Samsung have offered annual, similarly structured OLED lineups with entry-level, mid-range and high-end options, Sony's Bravia OLED series have only a limited number of options.</p><p>For example, the <a href="https://www.tomsguide.com/tvs/sony-bravia-8-oled-tv">Bravia 8 OLED</a> that launched in 2024, spent much of its life living alongside the higher-end, quantum dot-equipped <a href="https://www.tomsguide.com/reviews/sony-bravia-xr-a95l-qd-oled-tv">Sony A95L OLED</a>.</p><p>In 2025, the Bravia 8's successor — the <a href="https://www.tomsguide.com/tvs/oled-tvs/sony-bravia-8-ii-review">Bravia 8 II</a> — incorporated quantum dots, elevating it closer to the A95L's higher-end stratum. </p><div><blockquote><p>So far in 2026, Sony has yet to confirm the existence of a new OLED TV.</p></blockquote></div><p>But, so far in 2026, Sony has yet to confirm the existence of a new OLED TV, and currently, all three of these higher-end Sony-branded OLEDs are sitting on shelves simultaneously.</p><p>For the Bravia 6 to compete with mid-range OLEDs like the LG C6 and the Samsung S90H, it wouldn't necessarily need to be Sony's mid-range model <em>per se</em>; it would only need to hover around the same price point as its rivals. </p><p>Hopefully Sony isn't backing away from OLED anytime soon. It was worrying that the brand was content to leave its current OLED lineup on shelves for another calendar year — but, if real, the Bravia 6 would prove that Sony still sees a place for OLED alongside its True RGB TVs like the <a href="https://www.tomsguide.com/tvs/we-just-tested-the-bravia-7-ii-heres-why-i-think-it-could-be-the-sony-tv-to-buy-in-2026">Bravia 7 II</a> and <a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs">9 II</a>.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eJkrEW"></div>                            </div>                            <script src="https://kwizly.com/embed/eJkrEW.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/tvs/i-saw-sonys-true-rgb-up-close-and-the-downfall-of-oled-tvs-might-come-sooner-than-expected">I saw Sony's True RGB up close — and the downfall of OLED TVs might come sooner than expected</a></li><li><a href="https://www.tomsguide.com/tvs/tcl-and-sony-are-joining-forces-to-sell-more-tvs-but-is-that-bad-news-for-shoppers">TCL and Sony are joining forces to sell more TVs — but is that bad news for shoppers?</a></li><li><a href="https://www.tomsguide.com/tvs/we-just-tested-the-bravia-7-ii-heres-why-i-think-it-could-be-the-sony-tv-to-buy-in-2026">We just tested the Bravia 7 II — here’s why I think it could be the Sony TV to buy in 2026</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Sony Bravia Theatre Bar 6 review ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">Sony Bravia Theatre Bar 6 review: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price: </strong>$699 / £449<br><strong>Connectivity: </strong>HDMI eARC, Bluetooth<br><strong>Weight: </strong>33 lbs<br><strong>Dimensions: </strong>37.5 x 2.6 inches x 4.4 inches (Soundbar); 8.31 x 15.31 x 16.94 inches (Subwoofer)<br><strong>Colors: </strong>Black<br><strong>Subwoofer: </strong>Yes<br><strong>Dolby Atmos: </strong>Yes<br><strong>Channel: </strong>3.1.2</p></div></div><p>The world of  soundbars has gotten a whole lot better over the past few years. The likes of the <a href="https://www.tomsguide.com/reviews/sonos-beam-gen-2"><u>Sonos Beam Gen 2</u></a> have made it easier to get Dolby Atmos and a reasonably vibrant sound without having to spend thousands of dollars. Sony's take on this is the Sony Bravia Theatre Bar 6.</p><p>The Theatre Bar 6 is designed for those who want to cut out the fuss of improving their living room audio. Sure, it doesn't have fancy features like rear speakers, but it still comes with a wireless subwoofer for improved bass, and you can often find it for less than $600. That said, it's lacking some features that some of the <a href="https://www.tomsguide.com/audio/soundbars/best-soundbars"><u>best soundbars</u></a> offer. </p><p>Is the Sony Bravia Theatre Bar 6 worth buying, or should you look elsewhere for Atmos? I tested the Bravia Theater 6 Bar to find out.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-cheat-sheet"><span>Sony Bravia Theatre Bar 6 review: Cheat Sheet</span></h3><ul><li>What is it? A compact Dolby Atmos soundbar that comes with a wireless subwoofer</li><li>Who is it for? Folks who want to cut out the fuss of improving their living room audio.</li><li>What does it cost? $699 / £449</li><li>What do we like? Its sleek design and inclusion of a wireless subwoofer</li><li>What don’t we like? The Bar 6 leaves a lot to be desired in the low end and it’s missing wireless features like AirPlay 2.</li></ul><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-price-and-availability"><span>Sony Bravia Theatre Bar 6 review: Price and availability</span></h3><p>Sony offers the Bravia Theatre Bar 6 at $699 or £449, which puts it squarely in mid-range territory — comfortably above the budget options, but also far below the high-end alternatives, including some of the options in Sony's lineup.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-design"><span>Sony Bravia Theatre Bar 6 review: Design</span></h3><p>As a mid-range soundbar, the Sony Bravia Theatre Bar 6 is pretty no-fuss in terms of design and setup. It’s a sleek black bar that measures 37.5 inches wide, 2.6 inches high, and 4.4 inches deep. That's bigger than some of the ultra-compact soundbar models out there, but also more compact than larger models like the <a href="https://www.tomsguide.com/audio/soundbars/sonos-arc-ultra-review"><u>Sonos Arc Ultra</u></a>. It was able to fit between the legs of a 65-inch TV that I tested it with, but only just, and if your TV is smaller, or even if you have a model with narrower leg placement, you might need to move the soundbar in front of those legs instead of between them. </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="Q5ReeJFpjty4bpcvFk38ig" name="Sony Bravia Theatre Bar 6--4" alt="Sony Bravia Theatre Bar 6 on a TV stand" src="https://cdn.mos.cms.futurecdn.net/Q5ReeJFpjty4bpcvFk38ig-1920-80.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>Sony actually includes a set of detachable feet that are designed to sit on the legs of some of its Bravia TVs, which is a nice touch for those in Sony's ecosystem.</p><p>On the top of the soundbar are small windows for the up-firing speakers, while the rest of the speakers' drivers fire out towards the front and sides. The front corners are rounded while the back corners are sharper, which is a nice design touch. On the back you'll find HDMI and optical ports, but that's it, apart from a power input. There’s only one HDMI port — so no HDMI passthrough.</p><p>The subwoofer offers a similar build quality and overall look to the soundbar, made from the same materials and in the same color. It is a little larger than the subwoofer that comes with some soundbars, but not overly so, measuring 8.31inches wide, 15.31 inches high, and 16.94 inches deep. You'll want to think about where you want to place it in your living room, though.</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="Xfos3zBtE9kMx8UXvnb3hg" name="Sony Bravia Theatre Bar 6--5" alt="The remote for a Sony Bravia Theatre Bar 6" src="https://cdn.mos.cms.futurecdn.net/Xfos3zBtE9kMx8UXvnb3hg-1920-80.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>Last but not least is the remote, which is very simple. It's small and offers controls for power, input, volume, bass volume, night mode, and mute. If you connect your soundbar through HDMI eARC, you probably won't need to use the included remote all that much, as you'll be able to change volume using your TV's remote. But if you do prefer a separate remote for the soundbar, you'll get one, which is nice.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-setup"><span>Sony Bravia Theatre Bar 6 review: Setup</span></h3><p>Setting everything up is incredibly easy. All you do is plug the power in and connect the HDMI port to your TV's HDMI eARC port. If you're using a compatible Sony Bravia TV, it'll detect and connect to it right away, but even if not, when connected through HDMI eARC, your TV should know to output audio to the soundbar.</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="wkmQZ6CuzUSZJPisGwzRcg" name="Sony Bravia Theatre Bar 6--2" alt="The Sony Bravia Theatre Bar 6 wireless subwoofer" src="https://cdn.mos.cms.futurecdn.net/wkmQZ6CuzUSZJPisGwzRcg-1920-80.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>Setting up the subwoofer is very easy too. All you have to do is plug it in and the soundbar should automatically connect to it without you actually having to do anything. </p><p>Once you've connected the soundbar to your TV, you'll want to connect it to the app, and this is very easy as well. You don't even actually have to create an account if you don't want to, though doing so will give you additional controls and features. After you download the Sony Bravia Connect app, you'll be walked through connecting the soundbar to the app through Bluetooth, which took me under one minute, as the app can automatically detect the soundbar and initiate a connection.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-connectivity-and-controls"><span>Sony Bravia Theatre Bar 6 review: Connectivity and controls</span></h3><p>One area where the Theatre Bar 6 doesn't quite match up to the competition is in connectivity. Of course, the main way to connect the soundbar is through HDMI eARC — which is how most soundbars connect to TVs these days. </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="jtAsugnoLLULkXiDW3STcg" name="Sony Bravia Theatre Bar 6--3" alt="Sony Bravia Theatre Bar 6 on a TV stand" src="https://cdn.mos.cms.futurecdn.net/jtAsugnoLLULkXiDW3STcg-1920-80.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>Where it's lacking is in <em>additional</em> connectivity. There's no 3.5mm port for connecting something like a phone through a wired connection. Plus, if you want to connect your phone wirelessly, you're stuck with Bluetooth 5.3 and not a Wi-Fi-based standard like Apple's AirPlay 2 or Google Cast. </p><p>Wi-Fi-based connectivity is an area where budget soundbars often compromise, but there are plenty of options in this price range like the Sonos Beam Gen 2, which does have more advanced wireless connectivity for those who want to integrate their soundbar with a multi-room setup. In other words, if you're looking for a soundbar that will work with other speakers for whole-home audio, the Sony Bravia Theatre Bar 6 probably isn't the soundbar for you.</p><p>The app in general is pretty bare bones, and that means that you won't get a ton of customization options. You will get controls for things like standby modes and DSEE, but you won't get an EQ that you can use to more finely tune the audio. That's a little disappointing.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-performance"><span>Sony Bravia Theatre Bar 6 review: Performance</span></h3><p>The Sony Bravia Theatre Bar 6 offers a 3.1.2 channel configuration, which includes three front-firing drivers coupled with two up-firing drivers and a wireless subwoofer for the low end. Those up-firing drivers allow it to support Dolby Atmos and DTS:X, which isn't necessarily a given in this price range, but also not uncommon.</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="hC8bxnZ2FTnLN8WZYcwWgg" name="Sony Bravia Theatre Bar 6--6" alt="Close up of the speaker on a Sony Bravia Theatre Bar 6" src="https://cdn.mos.cms.futurecdn.net/hC8bxnZ2FTnLN8WZYcwWgg-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I found the overall audio response to be quite good. The drivers and their placement made for an immersive sound overall, including for both music and movies. The soundstage was decently wide when listening to music, and while you can certainly get more immersion from systems with rear speakers, I found the height effect on offer by the Theatre Bar 6 to be better than expected for a mid-range soundbar. It helped make the sound effects in <em>Avengers: Endgame</em> more immersive than they otherwise would be, especially when it came to some of the higher-frequency effects during the final fight scene.</p><p>Now, to be clear, the height effect that you'll get on the Theatre Bar 6 isn't perfect — and it's not necessarily ultra-convincing when it comes to hearing specific elements in the mix, but you do at least get some immersion from the soundbar when watching movies in Dolby Atmos. In other words, while it definitely adds a sense of height to the mix, it doesn’t quite separate out those elements in a way that might allow you to pinpoint exactly what you’re hearing in those height channels – especially in dense mixes, like the aforementioned final scene in <em>Avengers: Endgame</em>. </p><p>The subwoofer definitely helps allow the system to produce deep frequencies compared to systems that don't come with a subwoofer, but I wouldn't say it's the most powerful subwoofer out there, despite its somewhat large size. It can get reasonably loud, but its frequency range is a little limited, meaning that it can sound a little muddy and overly boosted in the low mids and upper bass rather than extending deep into the bass spectrum. That’s especially true in already bass-heavy tracks, like Kendrick Lamar’s <em>Loyalty</em>, in which the bass somewhat overpowered other elements in the track.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-verdict"><span>Sony Bravia Theatre Bar 6 review: Verdict</span></h3><p>The Sony Bravia Theatre Bar 6 is a well-built, decently compact soundbar with a solid audio response overall. It offers good spatial audio for the price, and it's nice that it comes with a subwoofer at all, when plenty of others at this price point don't. That said, the subwoofer leaves a lot to be desired in the low end, and the high frequencies aren't as crisp as I would have liked. On top of that, the soundbar is missing wireless features like AirPlay 2, limiting how much it can integrate with the rest of your speaker ecosystem.</p><p>Those things don't mean it's not worth consideration — it just depends what your priorities are. If you want a no-fuss soundbar and don't care much about getting something with Wi-Fi connectivity that can support, then the Bravia Theatre Bar 6 is actually a pretty compelling option. But, if you want more in the way of features, and a slightly better-rounded audio response, then something like the Sonos Beam Gen 2 is worth going for instead — especially as it allows you to add a subwoofer down the line anyway.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/soundbars/sony-bravia-theatre-bar-6-review</link>
                                                                            <description>
                            <![CDATA[ The Sony Bravia Theatre Bar 6 is a good-sounding midrange soundbar that supports Dolby Atmos and comes with a wireless subwoofer. However, its performance and features aren’t quite up to the level as comparable systems. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">JPJ9sact2yGAeqTJbMQRga</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/xsLywPrYeVpTsUYSQ2UB7g-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 02 Jun 2026 12:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Soundbars]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Speakers]]></category>
                                                                                                                    <dc:creator><![CDATA[ Christian de Looper ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/cyHuThvGLCKTfcMnWJiY4Y-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Christian de Looper is a freelance writer who has covered every facet of consumer tech, including mobile, audio, home theater, computing, gaming, and even car tech. At Tom’s Guide, Christian covers TV and home theater tech, and has reviewed dozens of TVs, soundbars, and A/V receivers, including those from the likes of Samsung, Hisense, TCL, and Vizio. Christian has also covered numerous industry events, from CES to F8, and is an expert at writing reviews, buying guides, and news. Outside of Tom’s Guide, Christian’s byline can be seen at TechRadar, Forbes, Digital Trends, Business Insider, and more. Follow Christian on Twitter to keep up with all his tech coverage.&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/xsLywPrYeVpTsUYSQ2UB7g-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sony Bravia Theatre Bar 6 on a TV stand]]></media:description>                                                            <media:text><![CDATA[Sony Bravia Theatre Bar 6 on a TV stand]]></media:text>
                                <media:title type="plain"><![CDATA[Sony Bravia Theatre Bar 6 on a TV stand]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/xsLywPrYeVpTsUYSQ2UB7g-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">Sony Bravia Theatre Bar 6 review: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price: </strong>$699 / £449<br><strong>Connectivity: </strong>HDMI eARC, Bluetooth<br><strong>Weight: </strong>33 lbs<br><strong>Dimensions: </strong>37.5 x 2.6 inches x 4.4 inches (Soundbar); 8.31 x 15.31 x 16.94 inches (Subwoofer)<br><strong>Colors: </strong>Black<br><strong>Subwoofer: </strong>Yes<br><strong>Dolby Atmos: </strong>Yes<br><strong>Channel: </strong>3.1.2</p></div></div><p>The world of  soundbars has gotten a whole lot better over the past few years. The likes of the <a href="https://www.tomsguide.com/reviews/sonos-beam-gen-2"><u>Sonos Beam Gen 2</u></a> have made it easier to get Dolby Atmos and a reasonably vibrant sound without having to spend thousands of dollars. Sony's take on this is the Sony Bravia Theatre Bar 6.</p><p>The Theatre Bar 6 is designed for those who want to cut out the fuss of improving their living room audio. Sure, it doesn't have fancy features like rear speakers, but it still comes with a wireless subwoofer for improved bass, and you can often find it for less than $600. That said, it's lacking some features that some of the <a href="https://www.tomsguide.com/audio/soundbars/best-soundbars"><u>best soundbars</u></a> offer. </p><p>Is the Sony Bravia Theatre Bar 6 worth buying, or should you look elsewhere for Atmos? I tested the Bravia Theater 6 Bar to find out.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-cheat-sheet"><span>Sony Bravia Theatre Bar 6 review: Cheat Sheet</span></h3><ul><li>What is it? A compact Dolby Atmos soundbar that comes with a wireless subwoofer</li><li>Who is it for? Folks who want to cut out the fuss of improving their living room audio.</li><li>What does it cost? $699 / £449</li><li>What do we like? Its sleek design and inclusion of a wireless subwoofer</li><li>What don’t we like? The Bar 6 leaves a lot to be desired in the low end and it’s missing wireless features like AirPlay 2.</li></ul><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-price-and-availability"><span>Sony Bravia Theatre Bar 6 review: Price and availability</span></h3><p>Sony offers the Bravia Theatre Bar 6 at $699 or £449, which puts it squarely in mid-range territory — comfortably above the budget options, but also far below the high-end alternatives, including some of the options in Sony's lineup.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-design"><span>Sony Bravia Theatre Bar 6 review: Design</span></h3><p>As a mid-range soundbar, the Sony Bravia Theatre Bar 6 is pretty no-fuss in terms of design and setup. It’s a sleek black bar that measures 37.5 inches wide, 2.6 inches high, and 4.4 inches deep. That's bigger than some of the ultra-compact soundbar models out there, but also more compact than larger models like the <a href="https://www.tomsguide.com/audio/soundbars/sonos-arc-ultra-review"><u>Sonos Arc Ultra</u></a>. It was able to fit between the legs of a 65-inch TV that I tested it with, but only just, and if your TV is smaller, or even if you have a model with narrower leg placement, you might need to move the soundbar in front of those legs instead of between them. </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="Q5ReeJFpjty4bpcvFk38ig" name="Sony Bravia Theatre Bar 6--4" alt="Sony Bravia Theatre Bar 6 on a TV stand" src="https://cdn.mos.cms.futurecdn.net/Q5ReeJFpjty4bpcvFk38ig-1920-80.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>Sony actually includes a set of detachable feet that are designed to sit on the legs of some of its Bravia TVs, which is a nice touch for those in Sony's ecosystem.</p><p>On the top of the soundbar are small windows for the up-firing speakers, while the rest of the speakers' drivers fire out towards the front and sides. The front corners are rounded while the back corners are sharper, which is a nice design touch. On the back you'll find HDMI and optical ports, but that's it, apart from a power input. There’s only one HDMI port — so no HDMI passthrough.</p><p>The subwoofer offers a similar build quality and overall look to the soundbar, made from the same materials and in the same color. It is a little larger than the subwoofer that comes with some soundbars, but not overly so, measuring 8.31inches wide, 15.31 inches high, and 16.94 inches deep. You'll want to think about where you want to place it in your living room, though.</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="Xfos3zBtE9kMx8UXvnb3hg" name="Sony Bravia Theatre Bar 6--5" alt="The remote for a Sony Bravia Theatre Bar 6" src="https://cdn.mos.cms.futurecdn.net/Xfos3zBtE9kMx8UXvnb3hg-1920-80.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>Last but not least is the remote, which is very simple. It's small and offers controls for power, input, volume, bass volume, night mode, and mute. If you connect your soundbar through HDMI eARC, you probably won't need to use the included remote all that much, as you'll be able to change volume using your TV's remote. But if you do prefer a separate remote for the soundbar, you'll get one, which is nice.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-setup"><span>Sony Bravia Theatre Bar 6 review: Setup</span></h3><p>Setting everything up is incredibly easy. All you do is plug the power in and connect the HDMI port to your TV's HDMI eARC port. If you're using a compatible Sony Bravia TV, it'll detect and connect to it right away, but even if not, when connected through HDMI eARC, your TV should know to output audio to the soundbar.</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="wkmQZ6CuzUSZJPisGwzRcg" name="Sony Bravia Theatre Bar 6--2" alt="The Sony Bravia Theatre Bar 6 wireless subwoofer" src="https://cdn.mos.cms.futurecdn.net/wkmQZ6CuzUSZJPisGwzRcg-1920-80.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>Setting up the subwoofer is very easy too. All you have to do is plug it in and the soundbar should automatically connect to it without you actually having to do anything. </p><p>Once you've connected the soundbar to your TV, you'll want to connect it to the app, and this is very easy as well. You don't even actually have to create an account if you don't want to, though doing so will give you additional controls and features. After you download the Sony Bravia Connect app, you'll be walked through connecting the soundbar to the app through Bluetooth, which took me under one minute, as the app can automatically detect the soundbar and initiate a connection.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-connectivity-and-controls"><span>Sony Bravia Theatre Bar 6 review: Connectivity and controls</span></h3><p>One area where the Theatre Bar 6 doesn't quite match up to the competition is in connectivity. Of course, the main way to connect the soundbar is through HDMI eARC — which is how most soundbars connect to TVs these days. </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="jtAsugnoLLULkXiDW3STcg" name="Sony Bravia Theatre Bar 6--3" alt="Sony Bravia Theatre Bar 6 on a TV stand" src="https://cdn.mos.cms.futurecdn.net/jtAsugnoLLULkXiDW3STcg-1920-80.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>Where it's lacking is in <em>additional</em> connectivity. There's no 3.5mm port for connecting something like a phone through a wired connection. Plus, if you want to connect your phone wirelessly, you're stuck with Bluetooth 5.3 and not a Wi-Fi-based standard like Apple's AirPlay 2 or Google Cast. </p><p>Wi-Fi-based connectivity is an area where budget soundbars often compromise, but there are plenty of options in this price range like the Sonos Beam Gen 2, which does have more advanced wireless connectivity for those who want to integrate their soundbar with a multi-room setup. In other words, if you're looking for a soundbar that will work with other speakers for whole-home audio, the Sony Bravia Theatre Bar 6 probably isn't the soundbar for you.</p><p>The app in general is pretty bare bones, and that means that you won't get a ton of customization options. You will get controls for things like standby modes and DSEE, but you won't get an EQ that you can use to more finely tune the audio. That's a little disappointing.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-performance"><span>Sony Bravia Theatre Bar 6 review: Performance</span></h3><p>The Sony Bravia Theatre Bar 6 offers a 3.1.2 channel configuration, which includes three front-firing drivers coupled with two up-firing drivers and a wireless subwoofer for the low end. Those up-firing drivers allow it to support Dolby Atmos and DTS:X, which isn't necessarily a given in this price range, but also not uncommon.</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="hC8bxnZ2FTnLN8WZYcwWgg" name="Sony Bravia Theatre Bar 6--6" alt="Close up of the speaker on a Sony Bravia Theatre Bar 6" src="https://cdn.mos.cms.futurecdn.net/hC8bxnZ2FTnLN8WZYcwWgg-1920-80.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I found the overall audio response to be quite good. The drivers and their placement made for an immersive sound overall, including for both music and movies. The soundstage was decently wide when listening to music, and while you can certainly get more immersion from systems with rear speakers, I found the height effect on offer by the Theatre Bar 6 to be better than expected for a mid-range soundbar. It helped make the sound effects in <em>Avengers: Endgame</em> more immersive than they otherwise would be, especially when it came to some of the higher-frequency effects during the final fight scene.</p><p>Now, to be clear, the height effect that you'll get on the Theatre Bar 6 isn't perfect — and it's not necessarily ultra-convincing when it comes to hearing specific elements in the mix, but you do at least get some immersion from the soundbar when watching movies in Dolby Atmos. In other words, while it definitely adds a sense of height to the mix, it doesn’t quite separate out those elements in a way that might allow you to pinpoint exactly what you’re hearing in those height channels – especially in dense mixes, like the aforementioned final scene in <em>Avengers: Endgame</em>. </p><p>The subwoofer definitely helps allow the system to produce deep frequencies compared to systems that don't come with a subwoofer, but I wouldn't say it's the most powerful subwoofer out there, despite its somewhat large size. It can get reasonably loud, but its frequency range is a little limited, meaning that it can sound a little muddy and overly boosted in the low mids and upper bass rather than extending deep into the bass spectrum. That’s especially true in already bass-heavy tracks, like Kendrick Lamar’s <em>Loyalty</em>, in which the bass somewhat overpowered other elements in the track.</p><h3 class="article-body__section" id="section-sony-bravia-theatre-bar-6-review-verdict"><span>Sony Bravia Theatre Bar 6 review: Verdict</span></h3><p>The Sony Bravia Theatre Bar 6 is a well-built, decently compact soundbar with a solid audio response overall. It offers good spatial audio for the price, and it's nice that it comes with a subwoofer at all, when plenty of others at this price point don't. That said, the subwoofer leaves a lot to be desired in the low end, and the high frequencies aren't as crisp as I would have liked. On top of that, the soundbar is missing wireless features like AirPlay 2, limiting how much it can integrate with the rest of your speaker ecosystem.</p><p>Those things don't mean it's not worth consideration — it just depends what your priorities are. If you want a no-fuss soundbar and don't care much about getting something with Wi-Fi connectivity that can support, then the Bravia Theatre Bar 6 is actually a pretty compelling option. But, if you want more in the way of features, and a slightly better-rounded audio response, then something like the Sonos Beam Gen 2 is worth going for instead — especially as it allows you to add a subwoofer down the line anyway.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ PS5 State of Play June 2026 live updates — every game reveal and new trailer at latest PlayStation showcase ]]></title>
                                                                                                <dc:content><![CDATA[ <div class="live-content"><p>PlayStation State of Play for June 2025 is now over, and it was certainly a packed showcase with loads of new games announced, a bunch of release dates for highly anticipated titles, and plenty of exciting trailers to watch.</p><p>The biggest news was the confirmation of the new God of War: Laufey, a new title in the popular series that swaps Kratos for his wife Faye, as she battles through the afterlife of the gods. Other big reveals included Until Dawn 2, The Lost Wild, Stuntman: Hollywood and plenty more new titles.</p><p>Below you can find every single announcement from State of Play June 2026, along with all the trailers. So if you missed the showcase, our live coverage will guide you through all the biggest news and announcements. </p><h2 id="how-to-re-watch-state-of-play-for-june-2026">How to (re)watch State of Play for June 2026</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/cvh0xXmu0bs" allowfullscreen></iframe></div></div><p>Missed State of Play June 2026? Don't worry, you can use the VOD link above to catch up with the whole showcase available to rewatch. Or you can read on for our live coverage to pick out the trailers you want to watch.</p><h3 class="article-body__section" id="section-live-updates"><span>LIVE UPDATES</span></h3></div><div class="live-content"><time datetime="2026-06-02T11:55:46+00:00">June 2, 2026 – 7:55 AM</time><h2 id="welcome-2">Welcome! </h2><p>Good morning! Welcome to Tom's Guide's live coverage of the PS5 State of Play for June 2026. I'm Rory, a senior entertainment editor, and one of biggest gamers on staff. I'll be bring you all the last-minute rumors, and also guiding you through the State of Play itself when it begins later today. So, let's get into the good stuff...</p></div><div class="live-content"><time datetime="2026-06-02T12:09:13+00:00">June 2, 2026 – 8:09 AM</time><h2 id="the-one-game-we-can-bank-on-being-at-state-of-play">The one game we can bank on being at State of Play</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="brkRET2rLiiqB38ggQjQsW" name="wolverine-2025-state-of-play-ps5-game" alt="Marvel's Wolverine image showing Logan" src="https://cdn.mos.cms.futurecdn.net/brkRET2rLiiqB38ggQjQsW-1920-80.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: Sony)</span></figcaption></figure><p>There's a lot we don't know about today's State of Play, but Sony has confirmed one game that will present: Marvel's Wolverine. Set to release on September 15, this superhero action game comes from Insomniac Games, the developer of the PS5 Marvel's Spider-Man games, and it looks brutal in the best way possible.  </p><p>We've been waiting a long time to get our hands (claws?) on Wolverine. It was announced during the PlayStation Showcase in September 2021, and almost five years later, we're finally entering the home straight with launch just a few months away. I'm expecting to see more gameplay today, and maybe new story details. </p><p>My other prediction when it comes to Marvel's Wolverine is that PlayStation might confirm a limited-edition Wolverine-themed DualSense and maybe even console.</p></div><div class="live-content"><time datetime="2026-06-02T12:17:41+00:00">June 2, 2026 – 8:17 AM</time><h2 id="ps5-state-of-play-june-2026-everything-you-need-to-know">PS5 State of Play June 2026: Everything you need to know</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:920px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="YjQgFQT9J6kopyr5nu4evh" name="Playstation store.jpg" alt="PlayStation Store Holiday Sale" src="https://cdn.mos.cms.futurecdn.net/YjQgFQT9J6kopyr5nu4evh-1920-80.jpg" mos="" align="middle" fullscreen="" width="920" height="518" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>So, the key details for State of Play are as follows. The live-streamed presentation takes place today (Tuesday, June 2), and it will run for "more than 60 minutes." Sony is promising "updates, announcements, and gameplay reveals from top studios around the world" with at least some presence from its own PlayStation Studios. </p><p>The timings for State of Play June 2026 are as follows: </p><ul><li><strong>2 p.m. PT</strong></li><li><strong>5 p.m. ET</strong></li><li><strong>10 p.m. BST </strong></li><li><strong>11 p.m. CEST</strong></li><li><strong>6 a.m. JST (June 3)</strong></li><li><strong>7 a.m. AEDT (June 3)</strong></li></ul><p>You'll be able to watch along live via PlayStation's channels on YouTube and Twitch, but fear not if you can't get to a stream, that's what I'm here for. In this live blog, I'll bring you all the pre-show build-up, and full live coverage of every announcement. </p></div><div class="live-content"><time datetime="2026-06-02T12:25:27+00:00">June 2, 2026 – 8:25 AM</time><h2 id="you-can-watch-state-of-play-on-the-big-screen">You can watch State of Play on the big screen!</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="URqrajEiPL87bAUgpp89kZ" name="GettyImages-1177482185.jpg" alt="movie theater" src="https://cdn.mos.cms.futurecdn.net/URqrajEiPL87bAUgpp89kZ-1920-80.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: Baona/Getty Images)</span></figcaption></figure><p>How about this for the ultimate way to enjoy the State of Play? Sony has partnered with Alamo Drafthouse (a cinema chain the Japanese tech giant recently bought) to host State of Play fan events, showing the event on the big screen!</p><p><a href="https://drafthouse.com/event/free-fan-event-sony-state-of-play" target="_blank" rel="nofollow"><strong>Tickets are free</strong></a>, and the State of Play will be shown in Drafthouse's across the U.S., including in Los Angeles, New York, Austin, Boston and Chicago. I can't think of a better way to enjoy the occasion than watching all the announcements in a movie theater with an audience that will no doubt only add to the atmosphere. </p><p>I'll be watching at my desk (after all, I need to bring TG readers the latest updates!), but if you're fortunate enough to live near one of the locations hosting the event, check out the link above. Just be aware that tickets might have sold out.</p></div><div class="live-content"><time datetime="2026-06-02T12:32:29+00:00">June 2, 2026 – 8:32 AM</time><h2 id="would-you-like-fries-with-that">Would you like fries with that?</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="EtgXweYbB7JaJN9QQvWuA3" name="state-of-play-themed-menu" alt="State of Play themed menu at Alamo Drafthouse" src="https://cdn.mos.cms.futurecdn.net/EtgXweYbB7JaJN9QQvWuA3-1920-80.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: Daniel Ahmad)</span></figcaption></figure><p>Alongside screening the State of Play at select Alamo Drafthouse locations, it looks like there will be Wolverine themed-food on offer as well (source <a href="https://x.com/ZhugeEX/status/2061091369616105577?s=20" target="_blank">@ZhugeEX</a>). This includes "Savage Sticks," "Rage Pizza" and a "Mutant Instinct" cocktail. Just looking at the menu is making me hungry. I wonder if you can order these themed items on DoorDash?</p></div><div class="live-content"><time datetime="2026-06-02T12:45:01+00:00">June 2, 2026 – 8:45 AM</time><h2 id="here-s-when-i-think-we-ll-see-wolverine-during-state-of-play">Here's when I think we'll see Wolverine during State of Play</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="2s7pYFzTXPQbkDmreoQvoL" name="wolverine-ps5-promo-image" alt="Marvel's Wolverine screenshot showing Logan attacking an enemy" src="https://cdn.mos.cms.futurecdn.net/2s7pYFzTXPQbkDmreoQvoL-1920-80.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: Insomniac Games / Sony Interactive Entertainment)</span></figcaption></figure><p>We'll be diving into the world of rumors, leaks and speculation soon enough, but for now, let's keep the focus on what we do know. So, it's been confirmed that Marvel's Wolverine will feature, but the big question, is when will it be showcased? </p><p>My prediction is that Wolverine will open the show. For starters, PlayStation will want to lead off with a big game, which Wolverine most certainly is. Secondly, because Logan's involvement has been pre-confirmed, it makes sense to open the show, leaving the final reveal to be something unexpectedly and hype generating. </p><p>Furthermore, the opening slot is definitely a prime position, and it would make sense for PlayStation to reverse it for one of its own titles rather than a third-party.</p></div><div class="live-content"><time datetime="2026-06-02T13:01:50+00:00">June 2, 2026 – 9:01 AM</time><h2 id="the-next-god-of-war-could-be-revealed-today">The next God of War could be revealed today</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="AXNNW7PLmYmCxqswRjhu8R" name="God of War Ragnarok - Kratos vs creature.jpg" alt="God of War Ragnarok" src="https://cdn.mos.cms.futurecdn.net/AXNNW7PLmYmCxqswRjhu8R-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>There's one rumor that just won't go away ahead of the State of Play. Various sources, including <a href="https://www.youtube.com/watch?t=3513&v=NoxlKQRhF34&feature=youtu.be" target="_blank">Insider Gaming's Tom Henderson,</a> have claimed the next game in the God of War series will be announced during State of Play June 2026. </p><p>It's already been a big year for the GoW series. In the February State of Play, a remake of the original trilogy was revealed, and the side-scrolling spin-off title God of War: Sons of Sparta was announced and released the same day. But if the rumors are correct, PlayStation is already ready to show what's next for the series.</p><p>The unconfirmed suggestion is that the next God of War will be a prequel game focused on Faye, Kratos' second wife, whose death kicked off the events of God of War (2018). It's also rumored this title will be directed by Cory Barlog, who was the sole director on the reboot and a co-director on sequel God of War Ragnarök. </p><p>As with all rumors, I advise taking this one with a grain of salt, but there's certainly a lot of online noise about this. We'll find out if it's true in just a matter of hours.</p></div><div class="live-content"><time datetime="2026-06-02T13:20:20+00:00">June 2, 2026 – 9:20 AM</time><h2 id="fairgames-could-be-re-revealed-as-break-in">Fairgames could be re-revealed as Break 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Y2mAwzqCspkL6rk6eS5Zw8" name="fairgames-ps5-screenshot" alt="Fairgames promotional image" src="https://cdn.mos.cms.futurecdn.net/Y2mAwzqCspkL6rk6eS5Zw8-1920-80.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: Haven Studios / Sony Interactive Entertainment)</span></figcaption></figure><p>The upcoming live-service shooter Fairgames from new team Haven Studios was announced back in 2023, but we've not seen it since. Therefore, it stands to reason that it could resurface at today's State of Play, but perhaps with a new name.</p><p>Last month, it was reported that Sony had filed a trademark for a new game called "Break In," but the internet rumor mill, tipped off by a not-so-subtle clue from Bloomberg's Jason Schreier (per <a href="https://www.ign.com/articles/sony-files-trademark-for-new-game-called-break-in" target="_blank">IGN</a>), suggests this might not be a new game at all, but rather a rebranding of Fairgames. </p><p>Rightly or wrongly, the online reception to Fairgames was pretty mixed, with many fans suggesting it'll be the next Concord (a PlayStation live service game that was shut down within just a few weeks). So, giving it a new title and a fresh slate, and hoping to make a better impression this time around, could be a smart play.</p></div><div class="live-content"><time datetime="2026-06-02T13:40:33+00:00">June 2, 2026 – 9:40 AM</time><h2 id="i-need-to-see-more-from-intergalactic">I need to see more from Intergalactic!</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="gGHYy9kM2a4pZMfY2DzSSQ" name="intergalactic-the-heretic-prophet-screenshot" alt="Intergalactic: The Heretic Prophet screenshot" src="https://cdn.mos.cms.futurecdn.net/gGHYy9kM2a4pZMfY2DzSSQ-1920-80.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: Sony / Naughty Dog)</span></figcaption></figure><p>If there's one already-announced game I'm desperate to see at the State of Play, it's Intergalactic: The Heretic Prophet. Naughty Dog are a top 3 developer in my book, so anything with the ND logo has my instant attention, and Intergalactic looks epic. </p><p>We've not seen this one since it was revealed at The Game Awards in December 2024, and while a 2026 release date seems very unlikely, a fresh look during today's State of Play isn't out of the question. The game is a sci-fi title that stars a futuristic bounty hunter who becomes stuck on a planet cut off from contact for 600 years. </p><p>If I were a betting man, I think a second trailer at this year's Game Awards might be a more likely slot for Intergalactic to resurface but I'm keeping my fingers crossed.</p></div><div class="live-content"><time datetime="2026-06-02T13:57:07+00:00">June 2, 2026 – 9:57 AM</time><h2 id="the-state-of-play-hype-is-building-already">The State of Play hype is building already</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="kah7qoXLLHvHCTTWqYJnoZ" name="state-of-play-youtube-viewers-june-2-2026" alt="PlayStation's YouTube Channel showing number of viewing waiting for State of Play June 2026" src="https://cdn.mos.cms.futurecdn.net/kah7qoXLLHvHCTTWqYJnoZ-1920-80.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: Google)</span></figcaption></figure><p>There's no doubt that there's a lot of excitement in the air about today's State of Play. In fact, some 5,000 fans are already in the waiting room as we speak. The showcase might not go live for around seven hours, but that isn't stopping some viewers from booting up the stream and essentially staring at an empty screen!  </p></div><div class="live-content"><time datetime="2026-06-02T14:22:50+00:00">June 2, 2026 – 10:22 AM</time><h2 id="could-rayman-return-at-state-of-play">Could Rayman return at State of Play?</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="Ykv8FdP83PDfxrKegmZPnC" name="Rayman Legends Hero.jpg" alt="Rayman Legends screenshot" src="https://cdn.mos.cms.futurecdn.net/Ykv8FdP83PDfxrKegmZPnC-1920-80.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: Ubisoft)</span></figcaption></figure><p>Well, looks like the cat is out of the bag when it comes to Rayman. Unless somebody is playing a very cruel joke, it appears that Rayman Legends is getting a remake in the form of Rayman Legends: Retold. Could this be one of the State of Play announcements that has leaked a little early? It certainly seems possible.</p><p>Per <a href="https://insider-gaming.com/rayman-legends-retold-gameplay/" target="_blank">Insider Gaming</a>, Rayman Legends: Retold is a re-release of the original game launched in 2013. It reportedly swaps the original's 2D art style for a 2D.5 approach, and even better, it might come bundled with a copy of its predecessor, Rayman Origins (hot takes: Legends is very good, but I actually preferred Origins overall).</p><p>Rayman Legends has such a timeless look that I'm not convinced we <em>need </em>a remake, and thanks to a PS4 port, the game is already playable on PS5, but still, Rayman fans likely won't be complaining about the often-neglect heroes return.</p></div><div class="live-content"><time datetime="2026-06-02T14:42:33+00:00">June 2, 2026 – 10:42 AM</time><h2 id="i-got-hands-on-with-two-upcoming-ps5-accessories">I got hands-on with two upcoming PS5 accessories</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:2424px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="N6LTKNQi66tSXXqZMZpNSE" name="Sony Fightstick and Speakers-2" alt="PlayStation FlexStrike Wireless Fight Stick and Pulse Elevate Wireless Speakers in a glass cabinet" src="https://cdn.mos.cms.futurecdn.net/N6LTKNQi66tSXXqZMZpNSE-1920-80.jpg" mos="" align="middle" fullscreen="" width="2424" height="1363" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Tom's Guide)</span></figcaption></figure><p>If you're looking to fill some of the time until State of Play, read <a href="https://www.tomsguide.com/gaming/playstation/ps5-flexstrike-fight-stick-and-pulse-elevate-speakers-preview">my hands-on report</a> with the upcoming FlexStrike fight stick and the Pulse Elevate desktop speakers. </p><p>I recently took a trip to the PlayStation U.K. office to check out these new PS5 accessories, due for launch in 2026, and I came away seriously impressed with both. The FlexStrike in particular has become an accessory I'm eager to add to my gaming setup, especially as it launches on August 8 alongside Marvel Tokon.</p><p>We might even see more of these new products in today's State of Play!</p></div><div class="live-content"><time datetime="2026-06-02T14:59:31+00:00">June 2, 2026 – 10:59 AM</time><h2 id="i-m-taking-a-big-huff-of-hopium-let-s-talk-bloodborne">I'm taking a big huff of hopium, let's talk Bloodborne</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="NXxKpabzwq9kkLRRjzEUEn" name="bloodborne-ps4-screenshot-02.jpg" alt="Bloodborne screenshot" src="https://cdn.mos.cms.futurecdn.net/NXxKpabzwq9kkLRRjzEUEn-1920-80.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: FromSoftware / Sony)</span></figcaption></figure><p>If you'll let me indulge in a little wishing here, like many PlayStation fans, my dream announcement today would be something Bloodborne-related. Sequel, remaster, remake, <em>just </em>a 60 fps patch for the game on PS4, I'd take just about anything. </p><p>The 2015 action-RPG from developer From Software is quite simply a masterpiece, and much as I've loved replaying it on PS4 regularly over the years, I'd love either a proper follow-up or a remastered version that bumps the framerate and makes its haunting world look even prettier. </p><p>I certainly wouldn't bank on Bloodborne being present at today's State of Play, but if the name did pop up in pretty much any form, I'd be squealing with delight.</p></div><div class="live-content"><time datetime="2026-06-02T15:19:29+00:00">June 2, 2026 – 11:19 AM</time><h2 id="don-t-forget-about-the-future-games-show-this-week">Don't forget about the Future Games Show this week!</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="ksyUW3v9CrjneEmwwfDX2h" name="FGS.0312_hero1_1920x1080" alt="Future Games Show Summer Showcase 2026" src="https://cdn.mos.cms.futurecdn.net/ksyUW3v9CrjneEmwwfDX2h-1920-80.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>It's set to be a huge week for gaming news. There's today's PS5 State of Play, Friday brings Summer Game Fest 2026, and Sunday sees Microsoft take the stage with the Xbox Showcase. But don't forget about the <strong>Future Games Show</strong> on Saturday. </p><p>Hosted by Troy Baker and Alex Wilton, it takes place on Saturday, June 6 at 12 p.m. PT / 3 p.m. ET / 8 p.m. BST and will feature trailers and reveals for more than 50 games, from AAA to indie, including Halloween: The Game, WARDOGS and more. </p><p>It's going to be a great showcase, and all that wasn't enough, it'll be followed by FGS Live from Los Angeles, with more trailers, premieres and developer interviews.</p></div><div class="live-content"><time datetime="2026-06-02T15:44:42+00:00">June 2, 2026 – 11:44 AM</time><h2 id="a-spooky-prediction-i-m-feeling-confident-about">A spooky prediction I'm feeling confident about</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="Us6AjqR5vrdSCAa6xcKoa9" name="silent-hill-townfall-screenshot" alt="Silent Hill: Townfall screenshot" src="https://cdn.mos.cms.futurecdn.net/Us6AjqR5vrdSCAa6xcKoa9-1920-80.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: Konami / Annapurna Interactive)</span></figcaption></figure><p>I'm fairly confident we'll be seeing more from Silent Hill: Townfall in today's State of Play. We might even get a release date considering it's been slated for 2026. </p><p>My reasoning is twofold. Firstly, earlier today, an ESRB rating for the game was discovered via <a href="https://mp1st.com/news/silent-hill-townfall-esrb-rating-surfaces-ahead-state-of-play" target="_blank">MP1st</a> (no surprises here, it's rated M). This would suggest it's gearing up for launch if the game's already been submitted for an age rating. </p><p>Secondly, the last State of Play offered a first: PlayStation gave the stage to Konami to host its own mini-showcase within a SOP. A portion of the event was dedicated to new reveals from the publisher, including a first trailer for Silent Hill: Townfall. </p><p>If Townfall is going to hit its confirmed 2026 window, then it stands to reason we'll see more from it soon, and with PlayStation already showing its strong partnership with Konami, I reckon we'll see more from Silent Hill: Townfall later today.</p></div><div class="live-content"><time datetime="2026-06-02T16:00:35+00:00">June 2, 2026 – 12:00 PM</time><h2 id="just-5-hours-to-go">Just 5 hours to go! </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="ALK6jc8sVtnXdoztzrN4BT" name="state-of-play-june-2-2026-info" alt="State of Play info graphic showing timings for the June 2, 2026 event" src="https://cdn.mos.cms.futurecdn.net/ALK6jc8sVtnXdoztzrN4BT-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>It's not quite time to get seated and gets your snacks to hand, but we're <em>only </em>five hours away from the State of Play for June 2026 kicking off. Don't worry, we're not going anywhere, I'll be stepping back for a quick break before the show, but I'm tagging in some colleagues to keep you entertained as we get closer to the State of Play going live. For those curious how the waiting room is looking, it's dropped down to 2,300 viewers, but those numbers will explode as go-time approaches.</p></div><div class="live-content"><time datetime="2026-06-02T16:26:26+00:00">June 2, 2026 – 12:26 PM</time><h2 id="a-little-football">A little football</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="EsdXUwUzf3UFM8tWSAy4Yn" name="CFB27 cover" alt="College Football 27 deluxe edition cover" src="https://cdn.mos.cms.futurecdn.net/EsdXUwUzf3UFM8tWSAy4Yn-1920-80.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: EA Sports)</span></figcaption></figure><p>EA Sports just revealed the cover images for the third iteration of its revived College Football series. </p><p>CFB 27 will launch later this summer, so I wouldn't be surprised if we get a glimpse of the new game during State of Play. Chat will be chuffed I'm sure.</p></div><div class="live-content"><time datetime="2026-06-02T17:07:58+00:00">June 2, 2026 – 1:07 PM</time><h2 id="team-asobi-teases-sly-cooper">Team Asobi teases Sly Cooper</h2><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2061720031222759559"><p lang="en" dir="ltr">If you can’t find Sly, check the nearest safe.🦝😉#AstroBot #SlyCooper pic.twitter.com/CstQ6RbAwl<a href="https://twitter.com/cantworkitout/status/2061720031222759559">June 2, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>Team Asobi, the developers behind the excellent AstroBot, teased Sly Cooper in a little video earlier today. </p><p>It's no real guarantee but it is an interesting video to post on the day of a State of Play. Comments under the GIF are cautiously excited about a potential Sly 5. </p><p>What do you think, will we see the return of the raccoon thief today?</p></div><div class="live-content"><time datetime="2026-06-02T17:35:49+00:00">June 2, 2026 – 1:35 PM</time><h2 id="what-are-you-excited-to-maybe-see">What are you excited to maybe see?</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="cgx4DpiYHGNVhb5WsXGfSo" name="155615_0002 (1).jpg" alt="(L to R) King sits on Luz's shoulder waving as Luz stands with an excited smile and her hands clasped together in The Owl House Episode "Sense and Insensitivity"" src="https://cdn.mos.cms.futurecdn.net/cgx4DpiYHGNVhb5WsXGfSo-1920-80.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: Disney)</span></figcaption></figure><p>I have my own predictions but what do you think Sony will show off today? Or what do you want to see? </p><p>Let us know in the comments or shoot me an email at scott.younker@futurenet.com</p></div><div class="live-content"><time datetime="2026-06-02T18:02:13+00:00">June 2, 2026 – 2:02 PM</time><h2 id="a-fresh-look-at-horizon-hunters-gathering">A fresh look at Horizon Hunters Gathering</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="YhffejPLEhTLJYVz8sAmMn" name="horizon-hunters-gathering-ps5" alt="Horizon Hunters Gathering screenshot" src="https://cdn.mos.cms.futurecdn.net/YhffejPLEhTLJYVz8sAmMn-1920-80.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: Sony)</span></figcaption></figure><p>Sony announced Horizon Hunters Gathering back in February as a cooperative team game that sees players taking on different robo-dinos. </p><p>It seems cartoon-y, which isn't bad but maybe not what I personally want from the Horizon series. That said, if we're leaning into the silliness, I would love a sequel Lego Horizon game.</p><p>Anyway, it's very likely we'll get a fresh look at Hunters Gathering.</p></div><div class="live-content"><time datetime="2026-06-02T18:30:16+00:00">June 2, 2026 – 2:30 PM</time><h2 id="lara-croft-pops-up">Lara Croft pops up</h2><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2061868897276416408"><p lang="en" dir="ltr">New Tomb Raider: Legacy of Atlantis screenshots just went up on PlayStation Store: https://t.co/AuBEh3cIUzAppearance at State of Play later today? https://t.co/2TAczrHw0c(one more screenshot in the next reply) pic.twitter.com/D9o1pxee0c<a href="https://twitter.com/cantworkitout/status/2061868897276416408">June 2, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>Hmm, <a href="https://x.com/gematsu/status/2061868897276416408?s=20" target="_blank">Gematsu </a>noticed that new Tomb Raider: Legacy of Atlantis screenshots just popped up on the PlayStation Store.</p><p>This could mean that Lara Croft gets some shine today.</p><p>We don't have a release date for the next Tomb Raider game, so this could be a good opportunity for Sony to reveal one. </p><p>Some commenters pointed out that Summer Games Fest kicks off on June 5 this Friday. It's possible that we won't hear anything until then.</p></div><div class="live-content"><time datetime="2026-06-02T19:00:47+00:00">June 2, 2026 – 3:00 PM</time><h2 id="a-wishlist-of-sequels">A wishlist of sequels</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:1200px;"><p class="vanilla-image-block" style="padding-top:62.50%;"><img id="SK3bf8TwUHEtiiAZCZJJqH" name="khiii.jpg" alt="kingdom hearts" src="https://cdn.mos.cms.futurecdn.net/SK3bf8TwUHEtiiAZCZJJqH-1920-80.jpg" mos="" align="middle" fullscreen="" width="1200" height="750" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Square Enix)</span></figcaption></figure><p>I've been looking around Reddit, X and some forums and I keep seeing a wishlist of sequels. </p><p>Here are the most common ones:</p><ul><li>Bloodborne 2</li><li>Devil May Cry 6</li><li>Final Fantasy 7 number 3</li><li>God of War: Faye</li><li>Infamous 4</li><li>Kingdom Hearts 4</li><li>Nier 2</li><li>Uncharted 5</li><li>Twisted Metal something</li><li>Spider-Man 3</li></ul><p>Some are more realistic than others. Spider-Man 3 and Infamous for example are probably not showing up as Sucker Punch shows off Wolverine. </p><p>We have heard some inklings of a new Uncharted, but Naughty Dog has seemingly been a single game at a time studio for a couple decades now.</p><p>And Bloodborne 2 is just a dream to upset Bloodborne fans.</p></div><div class="live-content"><time datetime="2026-06-02T19:29:13+00:00">June 2, 2026 – 3:29 PM</time><h2 id="90-minutes-to-showtime">90 minutes to showtime</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:920px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="YjQgFQT9J6kopyr5nu4evh" name="Playstation store.jpg" alt="PlayStation Store Holiday Sale" src="https://cdn.mos.cms.futurecdn.net/YjQgFQT9J6kopyr5nu4evh-1920-80.jpg" mos="" align="middle" fullscreen="" width="920" height="518" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>As a reminder the show kicks off at<strong> at 2:00 p.m. PT / 5:00 p.m. ET / 10 p.m. BST.</strong> </p><p>That's 90 minutes from now. </p></div><div class="live-content"><time datetime="2026-06-02T19:55:37+00:00">June 2, 2026 – 3:55 PM</time><h2 id="mario-kart-is-about-to-have-some-extra-competition">Mario Kart is about to have some (extra) competition </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="zXXitwNxbyY7wHD8QBGSq3" name="yooka-laylee-kart-image" alt="Keyart image for Super Yooka-Laylee Kart" src="https://cdn.mos.cms.futurecdn.net/zXXitwNxbyY7wHD8QBGSq3-1920-80.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: Playtonic Games)</span></figcaption></figure><p>Hey all! It's Rory here, back from break to guide you through the final hour of build-up and, of course, give you all the live updates from the State of Play June 2026. </p><p>It looks like another soon-to-be-announced game has leaked, and this time, thanks to <a href="https://steamdb.info/app/4809050/" target="_blank">SteamDB</a>. The game in question is Super Yooka-Laylee Kart, presumably a kart racing spin-off from the 3D platforming series Yooka-Laylee. I guess the lizard-bat combo has already taken on Mario in the 3D collectathon arena, and now they're ready to take on Nintendo's mascot in the kart racing genre. </p><p>A kart racer seems a pretty natural next step for Yooka-Laylee, especially after the duo stepped into the 2D side-scrolling world for 2019's Impossible Lair. They've done platforming in 3D and 2D, so what's left to take to the race track? </p><p>However, there is some online speculation rumbling that this game might be announced at Friday's Summer Game Fest event rather than at the State of Play. We'll find out in just over an hour, but Yooka-Laylee fans, how are you feeling?</p></div><div class="live-content"><time datetime="2026-06-02T20:01:06+00:00">June 2, 2026 – 4:01 PM</time><h2 id="what-ps5-game-are-you-hoping-to-see-at-state-of-play">What PS5 game are you hoping to see at State of Play?</h2><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-X16BMe"></div>                            </div>                            <script src="https://kwizly.com/embed/X16BMe.js" async></script><p>I've given you a few of my wishes, and some heavily rumored games, but I want to hear from you. Vote in the poll above, and let me know what PS5 game you want to see at State of Play. And if you top pick isn't in the poll, let me in the comments!</p></div><div class="live-content"><time datetime="2026-06-02T20:16:45+00:00">June 2, 2026 – 4:16 PM</time><h2 id="tomb-raider-legacy-of-atlantis-may-have-been-delayed">Tomb Raider Legacy of Atlantis may have been delayed</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="pdLNJU9AXCKPZ2wnkm3SnG" name="tomb-raider-legacy-of-atlantis-image" alt="Tomb Raider: Legacy of Atlantis keyart image" src="https://cdn.mos.cms.futurecdn.net/pdLNJU9AXCKPZ2wnkm3SnG-1920-80.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: Crystal Dynamics / Flying Wild Hog)</span></figcaption></figure><p>Earlier today new screenshots for the upcoming Tomb Raider: Legacy of Atlantis (a remake of the first Tomb Raider game released in 1996) surfaced on the PlayStation Store, hinting we might get a new trailer at the State of Play. But Amazon just went a step further and appears to have leaked the release date and pre-order details (via <a href="https://x.com/RaidersSociety/status/2061884858205450602?s=20" target="_blank">@RaiderSociety</a>)</p><p>The bad news is, if this listing is accurate (Amazon has since removed it), the game has been delayed out of its previously 2026 window until February 10, 2027. But the listing appears to have confirmed a Deluxe Edition which includes a "post-launch DLC story pack" and a 48-hour early access period for players who pre-order. There's also a bonus outfit based on Lara's gear from the 2013 Tomb Raider reboot. </p><p>We'll find out soon if this listing was legit, but considering Amazon Game Studios is the publisher of both Tomb Raider: Legacy of Atlantis and the upcoming Tomb Raider: Catalyst (the next mainline entry in the series), the smart money says it is. </p></div><div class="live-content"><time datetime="2026-06-02T20:28:20+00:00">June 2, 2026 – 4:28 PM</time><h2 id="will-we-see-any-xbox-logos-during-today-s-state-of-play">Will we see any Xbox logos during today's State of Play?</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:809px;"><p class="vanilla-image-block" style="padding-top:56.37%;"><img id="ztD9L9fCjfUCfPmL5bJ3qd" name="xbox-logo-2" alt="Xbox logo" src="https://cdn.mos.cms.futurecdn.net/ztD9L9fCjfUCfPmL5bJ3qd-1920-80.jpg" mos="" align="middle" fullscreen="" width="809" height="456" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Microsoft)</span></figcaption></figure><p>One of the stranger topics of conversation this week has been the intense debate on comments from Xbox CEO Asha Sharma regarding previous Xbox Showcases opting to include the PS5 logo in trailers for games releasing on PS5. </p><p>Some Xbox fans believe this was a mistake, and Sharma appears to agree, posting on social media that she was "Seeing the feedback on logos. It was a miss, and I own it." (as reported by <a href="https://www.videogameschronicle.com/news/xbox-ceo-calls-ps5-logo-decision-a-miss-and-promises-to-rethink-games-showcase-policy/" target="_blank">VGC</a>). Personally, I think more transparency is a good thing. If a game is releasing on multiple platforms, why try to hide that fact? </p><p>That leads to today, and some humorous speculation on whether PlayStation will include the Xbox logo in its own State of Play. The history books suggest that's unlikely; PlayStation has historically excluded the Xbox logo when spotlighting multi-platform games. But for me, I'd rather all companies were just upfront. </p><p>If a game is landing on Xbox and PlayStation, let gamers know, don't hide it! </p></div><div class="live-content"><time datetime="2026-06-02T20:33:35+00:00">June 2, 2026 – 4:33 PM</time><h2 id="the-countdown-has-started-the-stream-is-live">The countdown has started, the stream is LIVE</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/cvh0xXmu0bs" allowfullscreen></iframe></div></div><p>This is when you know we're almost there! The PlayStation State of Play livestream has gone LIVE. Okay, so it's just a 30-minute countdown clock, but it at least gives us all some nice ambient music, and it means we're in the final home straight!</p></div><div class="live-content"><time datetime="2026-06-02T20:44:06+00:00">June 2, 2026 – 4:44 PM</time><h2 id="state-of-play-is-already-attracting-plenty-of-attention">State of Play is already attracting plenty of attention </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="Wwck8ERZDrcCnHLpXxkr4Q" name="ps5-state-of-play-june-2026-youtube-screencap" alt="YouTube screencap showing viewership figures for State of Play June 2026" src="https://cdn.mos.cms.futurecdn.net/Wwck8ERZDrcCnHLpXxkr4Q-1920-80.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: Google)</span></figcaption></figure><p>We're just under 20 minutes from the State of Play June 2026 beginning and already thousands of gamers are flooding to watch the countdown. Even in the time it took me to take the above screenshot and write this post, the viewing figures have jumped up to more than 82,000. Hopefully PlayStation has a suitably big show planned for all the thousands of people watching around the world.</p></div><div class="live-content"><time datetime="2026-06-02T20:50:33+00:00">June 2, 2026 – 4:50 PM</time><h2 id="10-minute-warning-now-is-the-time-to-get-ready">10 minute warning, now is the time to get ready!</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="ALK6jc8sVtnXdoztzrN4BT" name="state-of-play-june-2-2026-info" alt="State of Play info graphic showing timings for the June 2, 2026 event" src="https://cdn.mos.cms.futurecdn.net/ALK6jc8sVtnXdoztzrN4BT-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>There's just 10 minutes until PS5 State of Play for June 2026 begins. If you haven't already now is the time to get ready. Boot up a live stream (there's one embedded above), get your snacks/drinks prepared, and maybe take a quick bathroom break!</p></div><div class="live-content"><time datetime="2026-06-02T20:56:22+00:00">June 2, 2026 – 4:56 PM</time><h2 id="time-for-one-final-prediction">Time for one final prediction!</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="Hrpjom7VxZqWBjNmh3PKZC" name="media-molecule-image" alt="Media Molecule image showing characters from LittleBigPlanet, Tearaway and Dreams" src="https://cdn.mos.cms.futurecdn.net/Hrpjom7VxZqWBjNmh3PKZC-1920-80.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: Media Molecule / Sony)</span></figcaption></figure><p>With just five minutes to go, I've got time for a final prediction. I think we might <em>finally </em>see what's next from British developer Media Molecule. The Guildford-based studio behind the popular LittleBigPlanet series hasn't released a game since 2020's Dreams, and this creation tool experience was dropped back in 2023. It's been long enough now that I have a inkling we might see what's next from MM.</p></div><div class="live-content"><time datetime="2026-06-02T21:00:48+00:00">June 2, 2026 – 5:00 PM</time><h2 id="the-waiting-is-over-state-of-play-june-2025-begins-now">The waiting is over! State of Play June 2025 begins now!</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="pwng4PmYEyMUmsMmXTrcgR" name="playstation-stock-image-state-of-play.jpg" alt="State of Play July 2021" src="https://cdn.mos.cms.futurecdn.net/pwng4PmYEyMUmsMmXTrcgR-1920-80.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>After all the build-up, predictions, leaks and rumors, now is the time for PlayStation to do the talking as State of Play 2026 begins right now...</p></div><div class="live-content"><time datetime="2026-06-02T21:02:46+00:00">June 2, 2026 – 5:02 PM</time><h2 id="wolverine-opens-the-state-of-play">Wolverine opens the State of Play!</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="2s7pYFzTXPQbkDmreoQvoL" name="wolverine-ps5-promo-image" alt="Marvel's Wolverine screenshot showing Logan attacking an enemy" src="https://cdn.mos.cms.futurecdn.net/2s7pYFzTXPQbkDmreoQvoL-1920-80.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: Insomniac Games / Sony Interactive Entertainment)</span></figcaption></figure><p>I predicted this one, and it's Marvel's Wolverine that kicks off the State of Play, with new gameplay, and some fresh details about this upcoming superhero title!</p></div><div class="live-content"><time datetime="2026-06-02T21:06:38+00:00">June 2, 2026 – 5:06 PM</time><h2 id="wolverine-will-include-other-mutants">Wolverine will include other mutants</h2><p>The first look at proper gameplay from Wolverine confirms that Logan won't be alone with other mutants to encounter during the campaign, it looks like Jean Grey will be one of them. It's also as violent and bloody as any Wolverine fan could want! </p></div><div class="live-content"><time datetime="2026-06-02T21:11:33+00:00">June 2, 2026 – 5:11 PM</time><h2 id="wolverine-gets-state-of-play-off-to-a-strong-start">Wolverine gets State of Play off to a strong start!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/OiBo_NgYI5Q" allowfullscreen></iframe></div></div><p>That's a pretty action-packed way to kick off a State of Play. Wolverine looks great, but I do have some worries that most of what we saw was cutscenes, I'm hoping that actually <em>playing</em> this new Marvel game will be as fun as watching footage.</p></div><div class="live-content"><time datetime="2026-06-02T21:13:44+00:00">June 2, 2026 – 5:13 PM</time><h2 id="marvel-tokon-is-the-next-game-showcased">Marvel Tokon is the next game showcased</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/cen2Lbmmo_Q" allowfullscreen></iframe></div></div><p>We're in for a Marvel section, as after Wolverine, we move on to Marvel Tokon: Fight Souls, the new fighting game from Arc System Works. I recently got to play a few rounds of this superhero brawler during a trip to PlayStation's U.K. office, and it was very good fun. It's rocketed up my must-play list. It's launching on August 6, 2026.</p></div><div class="live-content"><time datetime="2026-06-02T21:16:10+00:00">June 2, 2026 – 5:16 PM</time><h2 id="marvel-time-is-over-time-for-rayman">Marvel time is over, time for Rayman! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/TKfG9hiueak" allowfullscreen></iframe></div></div><p>Whelp, the leaks were spot on, next up is the reveal of Rayman Legends Retold. A remake of the excellent Rayman Legends title, this reworked spin on the platforming classic sports a 2.5D look and some new 3D sections.</p><p>It's dropping on October 1 on PS5. </p></div><div class="live-content"><time datetime="2026-06-02T21:18:09+00:00">June 2, 2026 – 5:18 PM</time><h2 id="bancho-the-chef-is-the-next-game-revealed">Bancho the Chef is the next game revealed</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/nJP_ZiDYfxY" allowfullscreen></iframe></div></div><p>I'm not really sure what to make of this one, but it's a new game called Bancho the Chef, and it looks like it mixes cartoonish graphics with a narrative driven approach. </p></div><div class="live-content"><time datetime="2026-06-02T21:21:04+00:00">June 2, 2026 – 5:21 PM</time><h2 id="next-up-a-new-trailer-for-kemuri">Next up, a new trailer for Kemuri</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/yWMwRfJj8So" allowfullscreen></iframe></div></div><p>This game looks wild; it's called Kemuri: Hunt the Unseen, and it's dropping in 2027. You play a hunter in a city trying to track down paranormal forces. Naturally, combat is a big focus of the title, and the art style has definitely caught my eye.</p></div><div class="live-content"><time datetime="2026-06-02T21:22:55+00:00">June 2, 2026 – 5:22 PM</time><h2 id="tomb-raider-legacy-of-atlantis-gets-the-spotlight">Tomb Raider: Legacy of Atlantis gets the spotlight</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/0wEjbSYNUM8" allowfullscreen></iframe></div></div><p>Next up, it's a new trailer for the Tomb Raider remake, subtitled Legacy of Atlantis. It looks cinematic, as any good Tomb Raider game should, but sadly, unless the Amazon listing page leak was incorrect, we know a delay into 2027 is coming.</p><p>There is, the game releases on February 12, 2027!</p></div><div class="live-content"><time datetime="2026-06-02T21:26:29+00:00">June 2, 2026 – 5:26 PM</time><h2 id="trailer-for-the-lost-world-no-it-s-not-jurassic-park">Trailer for The Lost World (No, it's not Jurassic Park)</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/nlt4o-mLxyM" allowfullscreen></iframe></div></div><p>I thought this was our latest look at the previously announced Jurassic Park: Survival, but I was wrong. It's actually <em>another </em>dino-themed game called The Lost World. No prizes for guessing what movie franchise inspired this title.</p></div><div class="live-content"><time datetime="2026-06-02T21:27:29+00:00">June 2, 2026 – 5:27 PM</time><h2 id="some-quick-fire-trailers-now">Some quick fire trailers now!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/ZX8mHo0aSbc" allowfullscreen></iframe></div></div><p>We're getting a quick fire dose of trailers now, first up is another quick glimpse at Phantom Blade 0 (which launches in September), and in just a few seconds, we're heading over to check out Dune: Awakening. </p></div><div class="live-content"><time datetime="2026-06-02T21:29:13+00:00">June 2, 2026 – 5:29 PM</time><h2 id="dune-awakening-comes-to-ps5-on-september-22">Dune Awakening comes to PS5 on September 22</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/iCKflvFn4p4" allowfullscreen></iframe></div></div><p>Dune Awakening is console-bound and coming on September 22. New for the console edition is a single-player option, letting you explore the spice-rich deserts solo, if you'd rather not be pestered by online players doing silly emotes. </p></div><div class="live-content"><time datetime="2026-06-02T21:30:30+00:00">June 2, 2026 – 5:30 PM</time><h2 id="trailers-for-dynasty-warriors-3-remastered-and-no-rest-for-the-wicked">Trailers for Dynasty Warriors 3 Remastered and No Rest for the Wicked</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/UTVVw9fsHVA" allowfullscreen></iframe></div></div><p>I told you the trailers were coming fast now. We got a look at Dynasty Warriors 3: Complete Edition, followed by the news that No Rest For the Wicked is coming to PS5 with a major 1.0 update in October 2026.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/dzOnRaQVoeI" allowfullscreen></iframe></div></div></div><div class="live-content"><time datetime="2026-06-02T21:33:08+00:00">June 2, 2026 – 5:33 PM</time><h2 id="can-onimusha-way-of-the-sword-keep-up-capcom-s-hot-streak">Can Onimusha: Way of the Sword keep up Capcom's hot streak?</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/LNq35HHUtNc" allowfullscreen></iframe></div></div><p>Capcom is on an insane run right now, and I'm hoping that Onimusha: Way of the Sword will keep that hot streak going. It certainly looks promising and is due to land on PS5 on September 25, 2026. Even better, <strong>a demo for the game is dropping on PS5 today.</strong> That's my post State of Play plans sorted, I guess!</p></div><div class="live-content"><time datetime="2026-06-02T21:35:33+00:00">June 2, 2026 – 5:35 PM</time><h2 id="i-d-know-that-music-anywhere">I'd know that music anywhere!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/eXLfSEipn7I" allowfullscreen></iframe></div></div><p>Silent Hill: Townfall up next, I could tell just from the eerie musical chimes alone! This new entry in the survival-horror series takes the franchise to Scotland, and is played in first-person. I wasn't a huge fan of Silent Hill f (hot take, I know), but I'm hoping that Townfall will hook me in the way that Silent Hill 2 remake managed.</p><p>Silent Hill: Townfall is releasing on September 24, 2025 (just 1 day before Onimusha, September is going to be <em>so </em>packed with new games)</p></div><div class="live-content"><time datetime="2026-06-02T21:38:37+00:00">June 2, 2026 – 5:38 PM</time><h2 id="ace-combat-8-wings-of-theve-lands-in-october">Ace Combat 8: Wings of Theve lands in October</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/c2bgoQhlzqE" allowfullscreen></iframe></div></div><p>It feels like every game is dropping in September/October, no doubt they're all trying to get out of the way of GTA 6. Ace Combat 8: Wings of Theve is the latest title to confirm it's releasing into what is looking like a crowed couple of months.</p></div><div class="live-content"><time datetime="2026-06-02T21:42:01+00:00">June 2, 2026 – 5:42 PM</time><h2 id="it-s-a-knight-rider-game-or-is-it-back-to-the-future">It's a Knight Rider game? Or is it Back to the Future?</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/A7NZpeAmdxk" allowfullscreen></iframe></div></div><p>What sort of mashup is this! Saber just announced a new title called Hollywood Stuntman, where, as the game suggests, you play as a stunt driver on movie sets. There appear to be licensed flicks involved with Knight Rider and Back to the Future teased in the trailer. I'm kind of completely sold on this concept! </p></div><div class="live-content"><time datetime="2026-06-02T21:43:45+00:00">June 2, 2026 – 5:43 PM</time><h2 id="this-horror-game-makes-wolverine-look-pg">This horror game makes Wolverine look PG</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/TFHcVsPNbsY" allowfullscreen></iframe></div></div><p>Ill gets a new trailer, and it's bloody and violent, it makes the Wolverine gameplay from earlier look tame. Don't watch the above trailer if you can't stomach lots of guts and intestines. </p></div><div class="live-content"><time datetime="2026-06-02T21:44:54+00:00">June 2, 2026 – 5:44 PM</time><h2 id="control-resonant-gets-a-story-trailer-and-release-date">Control: Resonant gets a story trailer (and release date)</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/TAzxTMaA6j4" allowfullscreen></iframe></div></div><p>One of my most anticipated games of 2026, Control Resonant is out September 24, 2026. And I can't wait. I didn't even want to watch the trailer as I'm sold already! </p></div><div class="live-content"><time datetime="2026-06-02T21:47:17+00:00">June 2, 2026 – 5:47 PM</time><h2 id="marathon-season-2-takes-the-spotlight">Marathon Season 2 takes the spotlight</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/-WVhTcByDyY" allowfullscreen></iframe></div></div><p>Well, if you need a quick bathroom break, I guess this Marathon Season 2 trailer isn't a bad time for one. Seeing as the chat has been spamming "We Want Destiny 3" all day long, there are quite a few unhappy comments about it. Marathon Season 2 launches today with a free trial weekend, and the base game gets a discount.</p></div><div class="live-content"><time datetime="2026-06-02T21:49:26+00:00">June 2, 2026 – 5:49 PM</time><h2 id="runescape-is-arriving-on-ps5">Runescape is arriving on PS5</h2><p>My nostalgia levels are rising as for the first time a Runescape game is coming to PS5. Okay, it's not the base game but a spin-off, but still, Runescape on PS5! It's also be added to the PlayStation+ catalog on day one. </p><p>Sony's also confirming a new PlayStation Classics being added to PS+ including PSI-Ops, which has hit with another dose of childhood memories.</p></div><div class="live-content"><time datetime="2026-06-02T21:50:37+00:00">June 2, 2026 – 5:50 PM</time><h2 id="could-this-be-the-until-dawn-sequel">Could this be the Until Dawn Sequel?</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="swTtov7dnGkenzGkkFCfWA" name="until dawn trailer (3).jpg" alt="A screenshot showing characters from Until Dawn" src="https://cdn.mos.cms.futurecdn.net/swTtov7dnGkenzGkkFCfWA-1920-80.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: Sony / SuperMassive Games)</span></figcaption></figure><p>Rumors of an Until Dawn sequel have been ever-present for years, and this trailer that shows young adults having fun in paradise over sinister music is screaming Until Dawn to me! </p></div><div class="live-content"><time datetime="2026-06-02T21:52:15+00:00">June 2, 2026 – 5:52 PM</time><h2 id="yup-until-dawn-2-is-confirmed">Yup, Until Dawn 2 is confirmed</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/QpVZ9OvRLZI" allowfullscreen></iframe></div></div><p>And there you have it, in 2027 we are getting a follow-up to fan-favorite PS4 game Until Dawn 2. It's about a group of "fake" ghost hunters, who travel to a tropical island and find themselves trapped in a real nightmare. </p></div><div class="live-content"><time datetime="2026-06-02T21:53:36+00:00">June 2, 2026 – 5:53 PM</time><h2 id="the-final-reveal-will-be-20-minutes-long">The final reveal will be 20 minutes long!</h2><p>Wow, our last reveal of the night will be a "big" one, and it'll be 20 minutes of uninterrupted gameplay rather than merely a quick trailer or logo. Could it be the rumored God of War prequel starring Faye? </p><p>The Santa Monica Studio logo has appeared, I think it just might be...</p></div><div class="live-content"><time datetime="2026-06-02T21:56:25+00:00">June 2, 2026 – 5:56 PM</time><h2 id="that-s-god-of-war-alright">That's God of War alright!</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="ZjP4fqAEcPtL4Km8XsFF2d" name="state-of-play-june-2-god-of-war" alt="State of Play June 2026 screenshot" src="https://cdn.mos.cms.futurecdn.net/ZjP4fqAEcPtL4Km8XsFF2d-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>Yup, it looks like all the rumors were correct, our last reveal is a new God of War game, and it will be starring Faye. Who awakes <em>somewhere </em>after her death! </p></div><div class="live-content"><time datetime="2026-06-02T21:59:23+00:00">June 2, 2026 – 5:59 PM</time><h2 id="our-first-look-at-faye-in-action">Our first look at Faye in action!</h2><p>Whelp, she might not be Kratos, but Faye can certainly kick some butt. Our first look at this new God of War title (assuming it'll adopt the GoW name) sees Faye attacked by planet creatures and easily dispatch them with aggressive move set of her own </p></div><div class="live-content"><time datetime="2026-06-02T22:07:39+00:00">June 2, 2026 – 6:07 PM</time><h2 id="move-over-kratos-i-m-team-faye-now">Move over Kratos, I'm Team Faye now</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="X94e4UCURnQV3K4kkLeGgB" name="state-of-play-june-2-god-of-war-01" alt="state of play june 2 screenshot" src="https://cdn.mos.cms.futurecdn.net/X94e4UCURnQV3K4kkLeGgB-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>I wasn't totally sold on a God of War prequel/spin-off without Kratos, but this gameplay is convincing me quickly. I'm wondering if PlayStation is showing us 20 minutes of gameplay out of the gate, could this game be closer to release? </p></div><div class="live-content"><time datetime="2026-06-02T22:15:47+00:00">June 2, 2026 – 6:15 PM</time><h2 id="and-it-s-called-god-of-war-laufey">And it's called God of War: Laufey</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/HLMX2w3cwuE" allowfullscreen></iframe></div></div><p>And there you have it, God of War: Laufey is coming to PS5. Not even a release year, so I guess my prediction that showing us so much of it today was a sign we might be close(ish) to release was wrong. I expect this is probably a late 2027 game at least.</p></div><div class="live-content"><time datetime="2026-06-02T22:16:54+00:00">June 2, 2026 – 6:16 PM</time><h2 id="and-that-s-the-state-of-play-june-2026-wrapped-up">And that's the State of Play June 2026 wrapped up!</h2><p>God of War: Laufey concludes today's State of Play. It was a suitably epic ending!</p></div><div class="live-content"><time datetime="2026-06-02T22:19:57+00:00">June 2, 2026 – 6:19 PM</time><h2 id="give-me-your-rating-for-today-s-state-of-play">Give me your rating for today's State of Play!</h2><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-O6jBVO"></div>                            </div>                            <script src="https://kwizly.com/embed/O6jBVO.js" async></script><p>Now that State of Play is over, I want to know what you make of the showcase from PlayStation. I'm going with a solid B+. It was a good showing, but lacked major surprises, and I'm quite disappointed we didn't see Intergalactic whatsoever.  </p></div><div class="live-content"><time datetime="2026-06-02T22:53:47+00:00">June 2, 2026 – 6:53 PM</time><h2 id="what-was-missing">What was missing?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="oSXnTeeWyDNn8CXYkWPAwN" name="resi-9-switch-2" alt="Resident Evil" src="https://cdn.mos.cms.futurecdn.net/oSXnTeeWyDNn8CXYkWPAwN-1920-80.png" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>We put up a poll earlier asking what games you were excited to see. </p><p>Unfortunately, every game on the list (except Wolverine) didn't make an appearance. </p><p>That includes Bloodborne (anything), FF7, RE9 DLC, Intergalactic, Horizon, Ken or Fairgames. It's actually surprising how many games were missing. </p><p>I did mention earlier that Summer Games Fest is on Friday. So, perhaps we'll see some of these games at that show, especially since they'll likely be multiplatform compared to some of the games shown today.</p></div><div class="live-content"><time datetime="2026-06-02T23:34:32+00:00">June 2, 2026 – 7:34 PM</time><h2 id="most-exciting-announcement">Most exciting announcement</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/nlt4o-mLxyM" allowfullscreen></iframe></div></div><p>Yes, God of War and Wolverine took up most of the but I think there were a couple of trailers that looked interesting.</p><p>For me, the Jurassic Park-esque "The Lost Wild" has my interest. I'm not sure where it's going, but I am curious.</p><p>Additionally, there was the trailer for Kemori. Look, I'm a surprisingly simple man, it turns out, and any game where you can grind wires like a skater will pique my interest.</p><p>There were more but I wanted to call those two out. Anything catch your eye?</p></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/news/live/ps5-state-of-play-june-2026-live-updates</link>
                                                                            <description>
                            <![CDATA[ State of Play returns for June 2025, and this "more than 60-minute" showcase promises fresh updates and new PS5 game reveals. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">EuZbPBLjCe7bj8sT2WzjJo</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/YLQe3Dc5ZooPurHxRPgwWB-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 02 Jun 2026 12:04:32 +0000</pubDate>                                                                                                                                <updated>Tue, 02 Jun 2026 23:34:36 +0000</updated>
                                                                                                                                            <category><![CDATA[Playstation]]></category>
                                                    <category><![CDATA[Gaming]]></category>
                                                                                                <author><![CDATA[ rory.mellon@futurenet.com (Rory Mellon) ]]></author>                    <dc:creator><![CDATA[ Rory Mellon ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/5Rv6LtQXMj5JB4Eu8Lt4Sn-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ null ]]></dc:description>
                                                                                                        <dc:contributor><![CDATA[ Scott Younker ]]></dc:contributor>
                                                                    <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/YLQe3Dc5ZooPurHxRPgwWB-1920-80.jpg">
                                                            <media:credit><![CDATA[Sony Interactive Entertainment]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A screenshot for Marvel&#039;s Wolverine with a PS5 State of Play graphics overlay]]></media:description>                                                            <media:text><![CDATA[A screenshot for Marvel&#039;s Wolverine with a PS5 State of Play graphics overlay]]></media:text>
                                <media:title type="plain"><![CDATA[A screenshot for Marvel&#039;s Wolverine with a PS5 State of Play graphics overlay]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/YLQe3Dc5ZooPurHxRPgwWB-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div class="live-content"><p>PlayStation State of Play for June 2025 is now over, and it was certainly a packed showcase with loads of new games announced, a bunch of release dates for highly anticipated titles, and plenty of exciting trailers to watch.</p><p>The biggest news was the confirmation of the new God of War: Laufey, a new title in the popular series that swaps Kratos for his wife Faye, as she battles through the afterlife of the gods. Other big reveals included Until Dawn 2, The Lost Wild, Stuntman: Hollywood and plenty more new titles.</p><p>Below you can find every single announcement from State of Play June 2026, along with all the trailers. So if you missed the showcase, our live coverage will guide you through all the biggest news and announcements. </p><h2 id="how-to-re-watch-state-of-play-for-june-2026">How to (re)watch State of Play for June 2026</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/cvh0xXmu0bs" allowfullscreen></iframe></div></div><p>Missed State of Play June 2026? Don't worry, you can use the VOD link above to catch up with the whole showcase available to rewatch. Or you can read on for our live coverage to pick out the trailers you want to watch.</p><h3 class="article-body__section" id="section-live-updates"><span>LIVE UPDATES</span></h3></div><div class="live-content"><time datetime="2026-06-02T11:55:46+00:00">June 2, 2026 – 7:55 AM</time><h2 id="welcome-2">Welcome! </h2><p>Good morning! Welcome to Tom's Guide's live coverage of the PS5 State of Play for June 2026. I'm Rory, a senior entertainment editor, and one of biggest gamers on staff. I'll be bring you all the last-minute rumors, and also guiding you through the State of Play itself when it begins later today. So, let's get into the good stuff...</p></div><div class="live-content"><time datetime="2026-06-02T12:09:13+00:00">June 2, 2026 – 8:09 AM</time><h2 id="the-one-game-we-can-bank-on-being-at-state-of-play">The one game we can bank on being at State of Play</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="brkRET2rLiiqB38ggQjQsW" name="wolverine-2025-state-of-play-ps5-game" alt="Marvel's Wolverine image showing Logan" src="https://cdn.mos.cms.futurecdn.net/brkRET2rLiiqB38ggQjQsW-1920-80.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: Sony)</span></figcaption></figure><p>There's a lot we don't know about today's State of Play, but Sony has confirmed one game that will present: Marvel's Wolverine. Set to release on September 15, this superhero action game comes from Insomniac Games, the developer of the PS5 Marvel's Spider-Man games, and it looks brutal in the best way possible.  </p><p>We've been waiting a long time to get our hands (claws?) on Wolverine. It was announced during the PlayStation Showcase in September 2021, and almost five years later, we're finally entering the home straight with launch just a few months away. I'm expecting to see more gameplay today, and maybe new story details. </p><p>My other prediction when it comes to Marvel's Wolverine is that PlayStation might confirm a limited-edition Wolverine-themed DualSense and maybe even console.</p></div><div class="live-content"><time datetime="2026-06-02T12:17:41+00:00">June 2, 2026 – 8:17 AM</time><h2 id="ps5-state-of-play-june-2026-everything-you-need-to-know">PS5 State of Play June 2026: Everything you need to know</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:920px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="YjQgFQT9J6kopyr5nu4evh" name="Playstation store.jpg" alt="PlayStation Store Holiday Sale" src="https://cdn.mos.cms.futurecdn.net/YjQgFQT9J6kopyr5nu4evh-1920-80.jpg" mos="" align="middle" fullscreen="" width="920" height="518" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>So, the key details for State of Play are as follows. The live-streamed presentation takes place today (Tuesday, June 2), and it will run for "more than 60 minutes." Sony is promising "updates, announcements, and gameplay reveals from top studios around the world" with at least some presence from its own PlayStation Studios. </p><p>The timings for State of Play June 2026 are as follows: </p><ul><li><strong>2 p.m. PT</strong></li><li><strong>5 p.m. ET</strong></li><li><strong>10 p.m. BST </strong></li><li><strong>11 p.m. CEST</strong></li><li><strong>6 a.m. JST (June 3)</strong></li><li><strong>7 a.m. AEDT (June 3)</strong></li></ul><p>You'll be able to watch along live via PlayStation's channels on YouTube and Twitch, but fear not if you can't get to a stream, that's what I'm here for. In this live blog, I'll bring you all the pre-show build-up, and full live coverage of every announcement. </p></div><div class="live-content"><time datetime="2026-06-02T12:25:27+00:00">June 2, 2026 – 8:25 AM</time><h2 id="you-can-watch-state-of-play-on-the-big-screen">You can watch State of Play on the big screen!</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="URqrajEiPL87bAUgpp89kZ" name="GettyImages-1177482185.jpg" alt="movie theater" src="https://cdn.mos.cms.futurecdn.net/URqrajEiPL87bAUgpp89kZ-1920-80.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: Baona/Getty Images)</span></figcaption></figure><p>How about this for the ultimate way to enjoy the State of Play? Sony has partnered with Alamo Drafthouse (a cinema chain the Japanese tech giant recently bought) to host State of Play fan events, showing the event on the big screen!</p><p><a href="https://drafthouse.com/event/free-fan-event-sony-state-of-play" target="_blank" rel="nofollow"><strong>Tickets are free</strong></a>, and the State of Play will be shown in Drafthouse's across the U.S., including in Los Angeles, New York, Austin, Boston and Chicago. I can't think of a better way to enjoy the occasion than watching all the announcements in a movie theater with an audience that will no doubt only add to the atmosphere. </p><p>I'll be watching at my desk (after all, I need to bring TG readers the latest updates!), but if you're fortunate enough to live near one of the locations hosting the event, check out the link above. Just be aware that tickets might have sold out.</p></div><div class="live-content"><time datetime="2026-06-02T12:32:29+00:00">June 2, 2026 – 8:32 AM</time><h2 id="would-you-like-fries-with-that">Would you like fries with that?</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="EtgXweYbB7JaJN9QQvWuA3" name="state-of-play-themed-menu" alt="State of Play themed menu at Alamo Drafthouse" src="https://cdn.mos.cms.futurecdn.net/EtgXweYbB7JaJN9QQvWuA3-1920-80.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: Daniel Ahmad)</span></figcaption></figure><p>Alongside screening the State of Play at select Alamo Drafthouse locations, it looks like there will be Wolverine themed-food on offer as well (source <a href="https://x.com/ZhugeEX/status/2061091369616105577?s=20" target="_blank">@ZhugeEX</a>). This includes "Savage Sticks," "Rage Pizza" and a "Mutant Instinct" cocktail. Just looking at the menu is making me hungry. I wonder if you can order these themed items on DoorDash?</p></div><div class="live-content"><time datetime="2026-06-02T12:45:01+00:00">June 2, 2026 – 8:45 AM</time><h2 id="here-s-when-i-think-we-ll-see-wolverine-during-state-of-play">Here's when I think we'll see Wolverine during State of Play</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="2s7pYFzTXPQbkDmreoQvoL" name="wolverine-ps5-promo-image" alt="Marvel's Wolverine screenshot showing Logan attacking an enemy" src="https://cdn.mos.cms.futurecdn.net/2s7pYFzTXPQbkDmreoQvoL-1920-80.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: Insomniac Games / Sony Interactive Entertainment)</span></figcaption></figure><p>We'll be diving into the world of rumors, leaks and speculation soon enough, but for now, let's keep the focus on what we do know. So, it's been confirmed that Marvel's Wolverine will feature, but the big question, is when will it be showcased? </p><p>My prediction is that Wolverine will open the show. For starters, PlayStation will want to lead off with a big game, which Wolverine most certainly is. Secondly, because Logan's involvement has been pre-confirmed, it makes sense to open the show, leaving the final reveal to be something unexpectedly and hype generating. </p><p>Furthermore, the opening slot is definitely a prime position, and it would make sense for PlayStation to reverse it for one of its own titles rather than a third-party.</p></div><div class="live-content"><time datetime="2026-06-02T13:01:50+00:00">June 2, 2026 – 9:01 AM</time><h2 id="the-next-god-of-war-could-be-revealed-today">The next God of War could be revealed today</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="AXNNW7PLmYmCxqswRjhu8R" name="God of War Ragnarok - Kratos vs creature.jpg" alt="God of War Ragnarok" src="https://cdn.mos.cms.futurecdn.net/AXNNW7PLmYmCxqswRjhu8R-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>There's one rumor that just won't go away ahead of the State of Play. Various sources, including <a href="https://www.youtube.com/watch?t=3513&v=NoxlKQRhF34&feature=youtu.be" target="_blank">Insider Gaming's Tom Henderson,</a> have claimed the next game in the God of War series will be announced during State of Play June 2026. </p><p>It's already been a big year for the GoW series. In the February State of Play, a remake of the original trilogy was revealed, and the side-scrolling spin-off title God of War: Sons of Sparta was announced and released the same day. But if the rumors are correct, PlayStation is already ready to show what's next for the series.</p><p>The unconfirmed suggestion is that the next God of War will be a prequel game focused on Faye, Kratos' second wife, whose death kicked off the events of God of War (2018). It's also rumored this title will be directed by Cory Barlog, who was the sole director on the reboot and a co-director on sequel God of War Ragnarök. </p><p>As with all rumors, I advise taking this one with a grain of salt, but there's certainly a lot of online noise about this. We'll find out if it's true in just a matter of hours.</p></div><div class="live-content"><time datetime="2026-06-02T13:20:20+00:00">June 2, 2026 – 9:20 AM</time><h2 id="fairgames-could-be-re-revealed-as-break-in">Fairgames could be re-revealed as Break 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Y2mAwzqCspkL6rk6eS5Zw8" name="fairgames-ps5-screenshot" alt="Fairgames promotional image" src="https://cdn.mos.cms.futurecdn.net/Y2mAwzqCspkL6rk6eS5Zw8-1920-80.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: Haven Studios / Sony Interactive Entertainment)</span></figcaption></figure><p>The upcoming live-service shooter Fairgames from new team Haven Studios was announced back in 2023, but we've not seen it since. Therefore, it stands to reason that it could resurface at today's State of Play, but perhaps with a new name.</p><p>Last month, it was reported that Sony had filed a trademark for a new game called "Break In," but the internet rumor mill, tipped off by a not-so-subtle clue from Bloomberg's Jason Schreier (per <a href="https://www.ign.com/articles/sony-files-trademark-for-new-game-called-break-in" target="_blank">IGN</a>), suggests this might not be a new game at all, but rather a rebranding of Fairgames. </p><p>Rightly or wrongly, the online reception to Fairgames was pretty mixed, with many fans suggesting it'll be the next Concord (a PlayStation live service game that was shut down within just a few weeks). So, giving it a new title and a fresh slate, and hoping to make a better impression this time around, could be a smart play.</p></div><div class="live-content"><time datetime="2026-06-02T13:40:33+00:00">June 2, 2026 – 9:40 AM</time><h2 id="i-need-to-see-more-from-intergalactic">I need to see more from Intergalactic!</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="gGHYy9kM2a4pZMfY2DzSSQ" name="intergalactic-the-heretic-prophet-screenshot" alt="Intergalactic: The Heretic Prophet screenshot" src="https://cdn.mos.cms.futurecdn.net/gGHYy9kM2a4pZMfY2DzSSQ-1920-80.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: Sony / Naughty Dog)</span></figcaption></figure><p>If there's one already-announced game I'm desperate to see at the State of Play, it's Intergalactic: The Heretic Prophet. Naughty Dog are a top 3 developer in my book, so anything with the ND logo has my instant attention, and Intergalactic looks epic. </p><p>We've not seen this one since it was revealed at The Game Awards in December 2024, and while a 2026 release date seems very unlikely, a fresh look during today's State of Play isn't out of the question. The game is a sci-fi title that stars a futuristic bounty hunter who becomes stuck on a planet cut off from contact for 600 years. </p><p>If I were a betting man, I think a second trailer at this year's Game Awards might be a more likely slot for Intergalactic to resurface but I'm keeping my fingers crossed.</p></div><div class="live-content"><time datetime="2026-06-02T13:57:07+00:00">June 2, 2026 – 9:57 AM</time><h2 id="the-state-of-play-hype-is-building-already">The State of Play hype is building already</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="kah7qoXLLHvHCTTWqYJnoZ" name="state-of-play-youtube-viewers-june-2-2026" alt="PlayStation's YouTube Channel showing number of viewing waiting for State of Play June 2026" src="https://cdn.mos.cms.futurecdn.net/kah7qoXLLHvHCTTWqYJnoZ-1920-80.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: Google)</span></figcaption></figure><p>There's no doubt that there's a lot of excitement in the air about today's State of Play. In fact, some 5,000 fans are already in the waiting room as we speak. The showcase might not go live for around seven hours, but that isn't stopping some viewers from booting up the stream and essentially staring at an empty screen!  </p></div><div class="live-content"><time datetime="2026-06-02T14:22:50+00:00">June 2, 2026 – 10:22 AM</time><h2 id="could-rayman-return-at-state-of-play">Could Rayman return at State of Play?</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="Ykv8FdP83PDfxrKegmZPnC" name="Rayman Legends Hero.jpg" alt="Rayman Legends screenshot" src="https://cdn.mos.cms.futurecdn.net/Ykv8FdP83PDfxrKegmZPnC-1920-80.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: Ubisoft)</span></figcaption></figure><p>Well, looks like the cat is out of the bag when it comes to Rayman. Unless somebody is playing a very cruel joke, it appears that Rayman Legends is getting a remake in the form of Rayman Legends: Retold. Could this be one of the State of Play announcements that has leaked a little early? It certainly seems possible.</p><p>Per <a href="https://insider-gaming.com/rayman-legends-retold-gameplay/" target="_blank">Insider Gaming</a>, Rayman Legends: Retold is a re-release of the original game launched in 2013. It reportedly swaps the original's 2D art style for a 2D.5 approach, and even better, it might come bundled with a copy of its predecessor, Rayman Origins (hot takes: Legends is very good, but I actually preferred Origins overall).</p><p>Rayman Legends has such a timeless look that I'm not convinced we <em>need </em>a remake, and thanks to a PS4 port, the game is already playable on PS5, but still, Rayman fans likely won't be complaining about the often-neglect heroes return.</p></div><div class="live-content"><time datetime="2026-06-02T14:42:33+00:00">June 2, 2026 – 10:42 AM</time><h2 id="i-got-hands-on-with-two-upcoming-ps5-accessories">I got hands-on with two upcoming PS5 accessories</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:2424px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="N6LTKNQi66tSXXqZMZpNSE" name="Sony Fightstick and Speakers-2" alt="PlayStation FlexStrike Wireless Fight Stick and Pulse Elevate Wireless Speakers in a glass cabinet" src="https://cdn.mos.cms.futurecdn.net/N6LTKNQi66tSXXqZMZpNSE-1920-80.jpg" mos="" align="middle" fullscreen="" width="2424" height="1363" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Tom's Guide)</span></figcaption></figure><p>If you're looking to fill some of the time until State of Play, read <a href="https://www.tomsguide.com/gaming/playstation/ps5-flexstrike-fight-stick-and-pulse-elevate-speakers-preview">my hands-on report</a> with the upcoming FlexStrike fight stick and the Pulse Elevate desktop speakers. </p><p>I recently took a trip to the PlayStation U.K. office to check out these new PS5 accessories, due for launch in 2026, and I came away seriously impressed with both. The FlexStrike in particular has become an accessory I'm eager to add to my gaming setup, especially as it launches on August 8 alongside Marvel Tokon.</p><p>We might even see more of these new products in today's State of Play!</p></div><div class="live-content"><time datetime="2026-06-02T14:59:31+00:00">June 2, 2026 – 10:59 AM</time><h2 id="i-m-taking-a-big-huff-of-hopium-let-s-talk-bloodborne">I'm taking a big huff of hopium, let's talk Bloodborne</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="NXxKpabzwq9kkLRRjzEUEn" name="bloodborne-ps4-screenshot-02.jpg" alt="Bloodborne screenshot" src="https://cdn.mos.cms.futurecdn.net/NXxKpabzwq9kkLRRjzEUEn-1920-80.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: FromSoftware / Sony)</span></figcaption></figure><p>If you'll let me indulge in a little wishing here, like many PlayStation fans, my dream announcement today would be something Bloodborne-related. Sequel, remaster, remake, <em>just </em>a 60 fps patch for the game on PS4, I'd take just about anything. </p><p>The 2015 action-RPG from developer From Software is quite simply a masterpiece, and much as I've loved replaying it on PS4 regularly over the years, I'd love either a proper follow-up or a remastered version that bumps the framerate and makes its haunting world look even prettier. </p><p>I certainly wouldn't bank on Bloodborne being present at today's State of Play, but if the name did pop up in pretty much any form, I'd be squealing with delight.</p></div><div class="live-content"><time datetime="2026-06-02T15:19:29+00:00">June 2, 2026 – 11:19 AM</time><h2 id="don-t-forget-about-the-future-games-show-this-week">Don't forget about the Future Games Show this week!</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="ksyUW3v9CrjneEmwwfDX2h" name="FGS.0312_hero1_1920x1080" alt="Future Games Show Summer Showcase 2026" src="https://cdn.mos.cms.futurecdn.net/ksyUW3v9CrjneEmwwfDX2h-1920-80.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>It's set to be a huge week for gaming news. There's today's PS5 State of Play, Friday brings Summer Game Fest 2026, and Sunday sees Microsoft take the stage with the Xbox Showcase. But don't forget about the <strong>Future Games Show</strong> on Saturday. </p><p>Hosted by Troy Baker and Alex Wilton, it takes place on Saturday, June 6 at 12 p.m. PT / 3 p.m. ET / 8 p.m. BST and will feature trailers and reveals for more than 50 games, from AAA to indie, including Halloween: The Game, WARDOGS and more. </p><p>It's going to be a great showcase, and all that wasn't enough, it'll be followed by FGS Live from Los Angeles, with more trailers, premieres and developer interviews.</p></div><div class="live-content"><time datetime="2026-06-02T15:44:42+00:00">June 2, 2026 – 11:44 AM</time><h2 id="a-spooky-prediction-i-m-feeling-confident-about">A spooky prediction I'm feeling confident about</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="Us6AjqR5vrdSCAa6xcKoa9" name="silent-hill-townfall-screenshot" alt="Silent Hill: Townfall screenshot" src="https://cdn.mos.cms.futurecdn.net/Us6AjqR5vrdSCAa6xcKoa9-1920-80.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: Konami / Annapurna Interactive)</span></figcaption></figure><p>I'm fairly confident we'll be seeing more from Silent Hill: Townfall in today's State of Play. We might even get a release date considering it's been slated for 2026. </p><p>My reasoning is twofold. Firstly, earlier today, an ESRB rating for the game was discovered via <a href="https://mp1st.com/news/silent-hill-townfall-esrb-rating-surfaces-ahead-state-of-play" target="_blank">MP1st</a> (no surprises here, it's rated M). This would suggest it's gearing up for launch if the game's already been submitted for an age rating. </p><p>Secondly, the last State of Play offered a first: PlayStation gave the stage to Konami to host its own mini-showcase within a SOP. A portion of the event was dedicated to new reveals from the publisher, including a first trailer for Silent Hill: Townfall. </p><p>If Townfall is going to hit its confirmed 2026 window, then it stands to reason we'll see more from it soon, and with PlayStation already showing its strong partnership with Konami, I reckon we'll see more from Silent Hill: Townfall later today.</p></div><div class="live-content"><time datetime="2026-06-02T16:00:35+00:00">June 2, 2026 – 12:00 PM</time><h2 id="just-5-hours-to-go">Just 5 hours to go! </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="ALK6jc8sVtnXdoztzrN4BT" name="state-of-play-june-2-2026-info" alt="State of Play info graphic showing timings for the June 2, 2026 event" src="https://cdn.mos.cms.futurecdn.net/ALK6jc8sVtnXdoztzrN4BT-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>It's not quite time to get seated and gets your snacks to hand, but we're <em>only </em>five hours away from the State of Play for June 2026 kicking off. Don't worry, we're not going anywhere, I'll be stepping back for a quick break before the show, but I'm tagging in some colleagues to keep you entertained as we get closer to the State of Play going live. For those curious how the waiting room is looking, it's dropped down to 2,300 viewers, but those numbers will explode as go-time approaches.</p></div><div class="live-content"><time datetime="2026-06-02T16:26:26+00:00">June 2, 2026 – 12:26 PM</time><h2 id="a-little-football">A little football</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="EsdXUwUzf3UFM8tWSAy4Yn" name="CFB27 cover" alt="College Football 27 deluxe edition cover" src="https://cdn.mos.cms.futurecdn.net/EsdXUwUzf3UFM8tWSAy4Yn-1920-80.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: EA Sports)</span></figcaption></figure><p>EA Sports just revealed the cover images for the third iteration of its revived College Football series. </p><p>CFB 27 will launch later this summer, so I wouldn't be surprised if we get a glimpse of the new game during State of Play. Chat will be chuffed I'm sure.</p></div><div class="live-content"><time datetime="2026-06-02T17:07:58+00:00">June 2, 2026 – 1:07 PM</time><h2 id="team-asobi-teases-sly-cooper">Team Asobi teases Sly Cooper</h2><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2061720031222759559"><p lang="en" dir="ltr">If you can’t find Sly, check the nearest safe.🦝😉#AstroBot #SlyCooper pic.twitter.com/CstQ6RbAwl<a href="https://twitter.com/cantworkitout/status/2061720031222759559">June 2, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>Team Asobi, the developers behind the excellent AstroBot, teased Sly Cooper in a little video earlier today. </p><p>It's no real guarantee but it is an interesting video to post on the day of a State of Play. Comments under the GIF are cautiously excited about a potential Sly 5. </p><p>What do you think, will we see the return of the raccoon thief today?</p></div><div class="live-content"><time datetime="2026-06-02T17:35:49+00:00">June 2, 2026 – 1:35 PM</time><h2 id="what-are-you-excited-to-maybe-see">What are you excited to maybe see?</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="cgx4DpiYHGNVhb5WsXGfSo" name="155615_0002 (1).jpg" alt="(L to R) King sits on Luz's shoulder waving as Luz stands with an excited smile and her hands clasped together in The Owl House Episode "Sense and Insensitivity"" src="https://cdn.mos.cms.futurecdn.net/cgx4DpiYHGNVhb5WsXGfSo-1920-80.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: Disney)</span></figcaption></figure><p>I have my own predictions but what do you think Sony will show off today? Or what do you want to see? </p><p>Let us know in the comments or shoot me an email at scott.younker@futurenet.com</p></div><div class="live-content"><time datetime="2026-06-02T18:02:13+00:00">June 2, 2026 – 2:02 PM</time><h2 id="a-fresh-look-at-horizon-hunters-gathering">A fresh look at Horizon Hunters Gathering</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="YhffejPLEhTLJYVz8sAmMn" name="horizon-hunters-gathering-ps5" alt="Horizon Hunters Gathering screenshot" src="https://cdn.mos.cms.futurecdn.net/YhffejPLEhTLJYVz8sAmMn-1920-80.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: Sony)</span></figcaption></figure><p>Sony announced Horizon Hunters Gathering back in February as a cooperative team game that sees players taking on different robo-dinos. </p><p>It seems cartoon-y, which isn't bad but maybe not what I personally want from the Horizon series. That said, if we're leaning into the silliness, I would love a sequel Lego Horizon game.</p><p>Anyway, it's very likely we'll get a fresh look at Hunters Gathering.</p></div><div class="live-content"><time datetime="2026-06-02T18:30:16+00:00">June 2, 2026 – 2:30 PM</time><h2 id="lara-croft-pops-up">Lara Croft pops up</h2><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2061868897276416408"><p lang="en" dir="ltr">New Tomb Raider: Legacy of Atlantis screenshots just went up on PlayStation Store: https://t.co/AuBEh3cIUzAppearance at State of Play later today? https://t.co/2TAczrHw0c(one more screenshot in the next reply) pic.twitter.com/D9o1pxee0c<a href="https://twitter.com/cantworkitout/status/2061868897276416408">June 2, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>Hmm, <a href="https://x.com/gematsu/status/2061868897276416408?s=20" target="_blank">Gematsu </a>noticed that new Tomb Raider: Legacy of Atlantis screenshots just popped up on the PlayStation Store.</p><p>This could mean that Lara Croft gets some shine today.</p><p>We don't have a release date for the next Tomb Raider game, so this could be a good opportunity for Sony to reveal one. </p><p>Some commenters pointed out that Summer Games Fest kicks off on June 5 this Friday. It's possible that we won't hear anything until then.</p></div><div class="live-content"><time datetime="2026-06-02T19:00:47+00:00">June 2, 2026 – 3:00 PM</time><h2 id="a-wishlist-of-sequels">A wishlist of sequels</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:1200px;"><p class="vanilla-image-block" style="padding-top:62.50%;"><img id="SK3bf8TwUHEtiiAZCZJJqH" name="khiii.jpg" alt="kingdom hearts" src="https://cdn.mos.cms.futurecdn.net/SK3bf8TwUHEtiiAZCZJJqH-1920-80.jpg" mos="" align="middle" fullscreen="" width="1200" height="750" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Square Enix)</span></figcaption></figure><p>I've been looking around Reddit, X and some forums and I keep seeing a wishlist of sequels. </p><p>Here are the most common ones:</p><ul><li>Bloodborne 2</li><li>Devil May Cry 6</li><li>Final Fantasy 7 number 3</li><li>God of War: Faye</li><li>Infamous 4</li><li>Kingdom Hearts 4</li><li>Nier 2</li><li>Uncharted 5</li><li>Twisted Metal something</li><li>Spider-Man 3</li></ul><p>Some are more realistic than others. Spider-Man 3 and Infamous for example are probably not showing up as Sucker Punch shows off Wolverine. </p><p>We have heard some inklings of a new Uncharted, but Naughty Dog has seemingly been a single game at a time studio for a couple decades now.</p><p>And Bloodborne 2 is just a dream to upset Bloodborne fans.</p></div><div class="live-content"><time datetime="2026-06-02T19:29:13+00:00">June 2, 2026 – 3:29 PM</time><h2 id="90-minutes-to-showtime">90 minutes to showtime</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:920px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="YjQgFQT9J6kopyr5nu4evh" name="Playstation store.jpg" alt="PlayStation Store Holiday Sale" src="https://cdn.mos.cms.futurecdn.net/YjQgFQT9J6kopyr5nu4evh-1920-80.jpg" mos="" align="middle" fullscreen="" width="920" height="518" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>As a reminder the show kicks off at<strong> at 2:00 p.m. PT / 5:00 p.m. ET / 10 p.m. BST.</strong> </p><p>That's 90 minutes from now. </p></div><div class="live-content"><time datetime="2026-06-02T19:55:37+00:00">June 2, 2026 – 3:55 PM</time><h2 id="mario-kart-is-about-to-have-some-extra-competition">Mario Kart is about to have some (extra) competition </h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="zXXitwNxbyY7wHD8QBGSq3" name="yooka-laylee-kart-image" alt="Keyart image for Super Yooka-Laylee Kart" src="https://cdn.mos.cms.futurecdn.net/zXXitwNxbyY7wHD8QBGSq3-1920-80.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: Playtonic Games)</span></figcaption></figure><p>Hey all! It's Rory here, back from break to guide you through the final hour of build-up and, of course, give you all the live updates from the State of Play June 2026. </p><p>It looks like another soon-to-be-announced game has leaked, and this time, thanks to <a href="https://steamdb.info/app/4809050/" target="_blank">SteamDB</a>. The game in question is Super Yooka-Laylee Kart, presumably a kart racing spin-off from the 3D platforming series Yooka-Laylee. I guess the lizard-bat combo has already taken on Mario in the 3D collectathon arena, and now they're ready to take on Nintendo's mascot in the kart racing genre. </p><p>A kart racer seems a pretty natural next step for Yooka-Laylee, especially after the duo stepped into the 2D side-scrolling world for 2019's Impossible Lair. They've done platforming in 3D and 2D, so what's left to take to the race track? </p><p>However, there is some online speculation rumbling that this game might be announced at Friday's Summer Game Fest event rather than at the State of Play. We'll find out in just over an hour, but Yooka-Laylee fans, how are you feeling?</p></div><div class="live-content"><time datetime="2026-06-02T20:01:06+00:00">June 2, 2026 – 4:01 PM</time><h2 id="what-ps5-game-are-you-hoping-to-see-at-state-of-play">What PS5 game are you hoping to see at State of Play?</h2><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-X16BMe"></div>                            </div>                            <script src="https://kwizly.com/embed/X16BMe.js" async></script><p>I've given you a few of my wishes, and some heavily rumored games, but I want to hear from you. Vote in the poll above, and let me know what PS5 game you want to see at State of Play. And if you top pick isn't in the poll, let me in the comments!</p></div><div class="live-content"><time datetime="2026-06-02T20:16:45+00:00">June 2, 2026 – 4:16 PM</time><h2 id="tomb-raider-legacy-of-atlantis-may-have-been-delayed">Tomb Raider Legacy of Atlantis may have been delayed</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="pdLNJU9AXCKPZ2wnkm3SnG" name="tomb-raider-legacy-of-atlantis-image" alt="Tomb Raider: Legacy of Atlantis keyart image" src="https://cdn.mos.cms.futurecdn.net/pdLNJU9AXCKPZ2wnkm3SnG-1920-80.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: Crystal Dynamics / Flying Wild Hog)</span></figcaption></figure><p>Earlier today new screenshots for the upcoming Tomb Raider: Legacy of Atlantis (a remake of the first Tomb Raider game released in 1996) surfaced on the PlayStation Store, hinting we might get a new trailer at the State of Play. But Amazon just went a step further and appears to have leaked the release date and pre-order details (via <a href="https://x.com/RaidersSociety/status/2061884858205450602?s=20" target="_blank">@RaiderSociety</a>)</p><p>The bad news is, if this listing is accurate (Amazon has since removed it), the game has been delayed out of its previously 2026 window until February 10, 2027. But the listing appears to have confirmed a Deluxe Edition which includes a "post-launch DLC story pack" and a 48-hour early access period for players who pre-order. There's also a bonus outfit based on Lara's gear from the 2013 Tomb Raider reboot. </p><p>We'll find out soon if this listing was legit, but considering Amazon Game Studios is the publisher of both Tomb Raider: Legacy of Atlantis and the upcoming Tomb Raider: Catalyst (the next mainline entry in the series), the smart money says it is. </p></div><div class="live-content"><time datetime="2026-06-02T20:28:20+00:00">June 2, 2026 – 4:28 PM</time><h2 id="will-we-see-any-xbox-logos-during-today-s-state-of-play">Will we see any Xbox logos during today's State of Play?</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:809px;"><p class="vanilla-image-block" style="padding-top:56.37%;"><img id="ztD9L9fCjfUCfPmL5bJ3qd" name="xbox-logo-2" alt="Xbox logo" src="https://cdn.mos.cms.futurecdn.net/ztD9L9fCjfUCfPmL5bJ3qd-1920-80.jpg" mos="" align="middle" fullscreen="" width="809" height="456" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Microsoft)</span></figcaption></figure><p>One of the stranger topics of conversation this week has been the intense debate on comments from Xbox CEO Asha Sharma regarding previous Xbox Showcases opting to include the PS5 logo in trailers for games releasing on PS5. </p><p>Some Xbox fans believe this was a mistake, and Sharma appears to agree, posting on social media that she was "Seeing the feedback on logos. It was a miss, and I own it." (as reported by <a href="https://www.videogameschronicle.com/news/xbox-ceo-calls-ps5-logo-decision-a-miss-and-promises-to-rethink-games-showcase-policy/" target="_blank">VGC</a>). Personally, I think more transparency is a good thing. If a game is releasing on multiple platforms, why try to hide that fact? </p><p>That leads to today, and some humorous speculation on whether PlayStation will include the Xbox logo in its own State of Play. The history books suggest that's unlikely; PlayStation has historically excluded the Xbox logo when spotlighting multi-platform games. But for me, I'd rather all companies were just upfront. </p><p>If a game is landing on Xbox and PlayStation, let gamers know, don't hide it! </p></div><div class="live-content"><time datetime="2026-06-02T20:33:35+00:00">June 2, 2026 – 4:33 PM</time><h2 id="the-countdown-has-started-the-stream-is-live">The countdown has started, the stream is LIVE</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/cvh0xXmu0bs" allowfullscreen></iframe></div></div><p>This is when you know we're almost there! The PlayStation State of Play livestream has gone LIVE. Okay, so it's just a 30-minute countdown clock, but it at least gives us all some nice ambient music, and it means we're in the final home straight!</p></div><div class="live-content"><time datetime="2026-06-02T20:44:06+00:00">June 2, 2026 – 4:44 PM</time><h2 id="state-of-play-is-already-attracting-plenty-of-attention">State of Play is already attracting plenty of attention </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="Wwck8ERZDrcCnHLpXxkr4Q" name="ps5-state-of-play-june-2026-youtube-screencap" alt="YouTube screencap showing viewership figures for State of Play June 2026" src="https://cdn.mos.cms.futurecdn.net/Wwck8ERZDrcCnHLpXxkr4Q-1920-80.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: Google)</span></figcaption></figure><p>We're just under 20 minutes from the State of Play June 2026 beginning and already thousands of gamers are flooding to watch the countdown. Even in the time it took me to take the above screenshot and write this post, the viewing figures have jumped up to more than 82,000. Hopefully PlayStation has a suitably big show planned for all the thousands of people watching around the world.</p></div><div class="live-content"><time datetime="2026-06-02T20:50:33+00:00">June 2, 2026 – 4:50 PM</time><h2 id="10-minute-warning-now-is-the-time-to-get-ready">10 minute warning, now is the time to get ready!</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="ALK6jc8sVtnXdoztzrN4BT" name="state-of-play-june-2-2026-info" alt="State of Play info graphic showing timings for the June 2, 2026 event" src="https://cdn.mos.cms.futurecdn.net/ALK6jc8sVtnXdoztzrN4BT-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>There's just 10 minutes until PS5 State of Play for June 2026 begins. If you haven't already now is the time to get ready. Boot up a live stream (there's one embedded above), get your snacks/drinks prepared, and maybe take a quick bathroom break!</p></div><div class="live-content"><time datetime="2026-06-02T20:56:22+00:00">June 2, 2026 – 4:56 PM</time><h2 id="time-for-one-final-prediction">Time for one final prediction!</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="Hrpjom7VxZqWBjNmh3PKZC" name="media-molecule-image" alt="Media Molecule image showing characters from LittleBigPlanet, Tearaway and Dreams" src="https://cdn.mos.cms.futurecdn.net/Hrpjom7VxZqWBjNmh3PKZC-1920-80.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: Media Molecule / Sony)</span></figcaption></figure><p>With just five minutes to go, I've got time for a final prediction. I think we might <em>finally </em>see what's next from British developer Media Molecule. The Guildford-based studio behind the popular LittleBigPlanet series hasn't released a game since 2020's Dreams, and this creation tool experience was dropped back in 2023. It's been long enough now that I have a inkling we might see what's next from MM.</p></div><div class="live-content"><time datetime="2026-06-02T21:00:48+00:00">June 2, 2026 – 5:00 PM</time><h2 id="the-waiting-is-over-state-of-play-june-2025-begins-now">The waiting is over! State of Play June 2025 begins now!</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="pwng4PmYEyMUmsMmXTrcgR" name="playstation-stock-image-state-of-play.jpg" alt="State of Play July 2021" src="https://cdn.mos.cms.futurecdn.net/pwng4PmYEyMUmsMmXTrcgR-1920-80.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>After all the build-up, predictions, leaks and rumors, now is the time for PlayStation to do the talking as State of Play 2026 begins right now...</p></div><div class="live-content"><time datetime="2026-06-02T21:02:46+00:00">June 2, 2026 – 5:02 PM</time><h2 id="wolverine-opens-the-state-of-play">Wolverine opens the State of Play!</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="2s7pYFzTXPQbkDmreoQvoL" name="wolverine-ps5-promo-image" alt="Marvel's Wolverine screenshot showing Logan attacking an enemy" src="https://cdn.mos.cms.futurecdn.net/2s7pYFzTXPQbkDmreoQvoL-1920-80.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: Insomniac Games / Sony Interactive Entertainment)</span></figcaption></figure><p>I predicted this one, and it's Marvel's Wolverine that kicks off the State of Play, with new gameplay, and some fresh details about this upcoming superhero title!</p></div><div class="live-content"><time datetime="2026-06-02T21:06:38+00:00">June 2, 2026 – 5:06 PM</time><h2 id="wolverine-will-include-other-mutants">Wolverine will include other mutants</h2><p>The first look at proper gameplay from Wolverine confirms that Logan won't be alone with other mutants to encounter during the campaign, it looks like Jean Grey will be one of them. It's also as violent and bloody as any Wolverine fan could want! </p></div><div class="live-content"><time datetime="2026-06-02T21:11:33+00:00">June 2, 2026 – 5:11 PM</time><h2 id="wolverine-gets-state-of-play-off-to-a-strong-start">Wolverine gets State of Play off to a strong start!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/OiBo_NgYI5Q" allowfullscreen></iframe></div></div><p>That's a pretty action-packed way to kick off a State of Play. Wolverine looks great, but I do have some worries that most of what we saw was cutscenes, I'm hoping that actually <em>playing</em> this new Marvel game will be as fun as watching footage.</p></div><div class="live-content"><time datetime="2026-06-02T21:13:44+00:00">June 2, 2026 – 5:13 PM</time><h2 id="marvel-tokon-is-the-next-game-showcased">Marvel Tokon is the next game showcased</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/cen2Lbmmo_Q" allowfullscreen></iframe></div></div><p>We're in for a Marvel section, as after Wolverine, we move on to Marvel Tokon: Fight Souls, the new fighting game from Arc System Works. I recently got to play a few rounds of this superhero brawler during a trip to PlayStation's U.K. office, and it was very good fun. It's rocketed up my must-play list. It's launching on August 6, 2026.</p></div><div class="live-content"><time datetime="2026-06-02T21:16:10+00:00">June 2, 2026 – 5:16 PM</time><h2 id="marvel-time-is-over-time-for-rayman">Marvel time is over, time for Rayman! </h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/TKfG9hiueak" allowfullscreen></iframe></div></div><p>Whelp, the leaks were spot on, next up is the reveal of Rayman Legends Retold. A remake of the excellent Rayman Legends title, this reworked spin on the platforming classic sports a 2.5D look and some new 3D sections.</p><p>It's dropping on October 1 on PS5. </p></div><div class="live-content"><time datetime="2026-06-02T21:18:09+00:00">June 2, 2026 – 5:18 PM</time><h2 id="bancho-the-chef-is-the-next-game-revealed">Bancho the Chef is the next game revealed</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/nJP_ZiDYfxY" allowfullscreen></iframe></div></div><p>I'm not really sure what to make of this one, but it's a new game called Bancho the Chef, and it looks like it mixes cartoonish graphics with a narrative driven approach. </p></div><div class="live-content"><time datetime="2026-06-02T21:21:04+00:00">June 2, 2026 – 5:21 PM</time><h2 id="next-up-a-new-trailer-for-kemuri">Next up, a new trailer for Kemuri</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/yWMwRfJj8So" allowfullscreen></iframe></div></div><p>This game looks wild; it's called Kemuri: Hunt the Unseen, and it's dropping in 2027. You play a hunter in a city trying to track down paranormal forces. Naturally, combat is a big focus of the title, and the art style has definitely caught my eye.</p></div><div class="live-content"><time datetime="2026-06-02T21:22:55+00:00">June 2, 2026 – 5:22 PM</time><h2 id="tomb-raider-legacy-of-atlantis-gets-the-spotlight">Tomb Raider: Legacy of Atlantis gets the spotlight</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/0wEjbSYNUM8" allowfullscreen></iframe></div></div><p>Next up, it's a new trailer for the Tomb Raider remake, subtitled Legacy of Atlantis. It looks cinematic, as any good Tomb Raider game should, but sadly, unless the Amazon listing page leak was incorrect, we know a delay into 2027 is coming.</p><p>There is, the game releases on February 12, 2027!</p></div><div class="live-content"><time datetime="2026-06-02T21:26:29+00:00">June 2, 2026 – 5:26 PM</time><h2 id="trailer-for-the-lost-world-no-it-s-not-jurassic-park">Trailer for The Lost World (No, it's not Jurassic Park)</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/nlt4o-mLxyM" allowfullscreen></iframe></div></div><p>I thought this was our latest look at the previously announced Jurassic Park: Survival, but I was wrong. It's actually <em>another </em>dino-themed game called The Lost World. No prizes for guessing what movie franchise inspired this title.</p></div><div class="live-content"><time datetime="2026-06-02T21:27:29+00:00">June 2, 2026 – 5:27 PM</time><h2 id="some-quick-fire-trailers-now">Some quick fire trailers now!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/ZX8mHo0aSbc" allowfullscreen></iframe></div></div><p>We're getting a quick fire dose of trailers now, first up is another quick glimpse at Phantom Blade 0 (which launches in September), and in just a few seconds, we're heading over to check out Dune: Awakening. </p></div><div class="live-content"><time datetime="2026-06-02T21:29:13+00:00">June 2, 2026 – 5:29 PM</time><h2 id="dune-awakening-comes-to-ps5-on-september-22">Dune Awakening comes to PS5 on September 22</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/iCKflvFn4p4" allowfullscreen></iframe></div></div><p>Dune Awakening is console-bound and coming on September 22. New for the console edition is a single-player option, letting you explore the spice-rich deserts solo, if you'd rather not be pestered by online players doing silly emotes. </p></div><div class="live-content"><time datetime="2026-06-02T21:30:30+00:00">June 2, 2026 – 5:30 PM</time><h2 id="trailers-for-dynasty-warriors-3-remastered-and-no-rest-for-the-wicked">Trailers for Dynasty Warriors 3 Remastered and No Rest for the Wicked</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/UTVVw9fsHVA" allowfullscreen></iframe></div></div><p>I told you the trailers were coming fast now. We got a look at Dynasty Warriors 3: Complete Edition, followed by the news that No Rest For the Wicked is coming to PS5 with a major 1.0 update in October 2026.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/dzOnRaQVoeI" allowfullscreen></iframe></div></div></div><div class="live-content"><time datetime="2026-06-02T21:33:08+00:00">June 2, 2026 – 5:33 PM</time><h2 id="can-onimusha-way-of-the-sword-keep-up-capcom-s-hot-streak">Can Onimusha: Way of the Sword keep up Capcom's hot streak?</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/LNq35HHUtNc" allowfullscreen></iframe></div></div><p>Capcom is on an insane run right now, and I'm hoping that Onimusha: Way of the Sword will keep that hot streak going. It certainly looks promising and is due to land on PS5 on September 25, 2026. Even better, <strong>a demo for the game is dropping on PS5 today.</strong> That's my post State of Play plans sorted, I guess!</p></div><div class="live-content"><time datetime="2026-06-02T21:35:33+00:00">June 2, 2026 – 5:35 PM</time><h2 id="i-d-know-that-music-anywhere">I'd know that music anywhere!</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/eXLfSEipn7I" allowfullscreen></iframe></div></div><p>Silent Hill: Townfall up next, I could tell just from the eerie musical chimes alone! This new entry in the survival-horror series takes the franchise to Scotland, and is played in first-person. I wasn't a huge fan of Silent Hill f (hot take, I know), but I'm hoping that Townfall will hook me in the way that Silent Hill 2 remake managed.</p><p>Silent Hill: Townfall is releasing on September 24, 2025 (just 1 day before Onimusha, September is going to be <em>so </em>packed with new games)</p></div><div class="live-content"><time datetime="2026-06-02T21:38:37+00:00">June 2, 2026 – 5:38 PM</time><h2 id="ace-combat-8-wings-of-theve-lands-in-october">Ace Combat 8: Wings of Theve lands in October</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/c2bgoQhlzqE" allowfullscreen></iframe></div></div><p>It feels like every game is dropping in September/October, no doubt they're all trying to get out of the way of GTA 6. Ace Combat 8: Wings of Theve is the latest title to confirm it's releasing into what is looking like a crowed couple of months.</p></div><div class="live-content"><time datetime="2026-06-02T21:42:01+00:00">June 2, 2026 – 5:42 PM</time><h2 id="it-s-a-knight-rider-game-or-is-it-back-to-the-future">It's a Knight Rider game? Or is it Back to the Future?</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/A7NZpeAmdxk" allowfullscreen></iframe></div></div><p>What sort of mashup is this! Saber just announced a new title called Hollywood Stuntman, where, as the game suggests, you play as a stunt driver on movie sets. There appear to be licensed flicks involved with Knight Rider and Back to the Future teased in the trailer. I'm kind of completely sold on this concept! </p></div><div class="live-content"><time datetime="2026-06-02T21:43:45+00:00">June 2, 2026 – 5:43 PM</time><h2 id="this-horror-game-makes-wolverine-look-pg">This horror game makes Wolverine look PG</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/TFHcVsPNbsY" allowfullscreen></iframe></div></div><p>Ill gets a new trailer, and it's bloody and violent, it makes the Wolverine gameplay from earlier look tame. Don't watch the above trailer if you can't stomach lots of guts and intestines. </p></div><div class="live-content"><time datetime="2026-06-02T21:44:54+00:00">June 2, 2026 – 5:44 PM</time><h2 id="control-resonant-gets-a-story-trailer-and-release-date">Control: Resonant gets a story trailer (and release date)</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/TAzxTMaA6j4" allowfullscreen></iframe></div></div><p>One of my most anticipated games of 2026, Control Resonant is out September 24, 2026. And I can't wait. I didn't even want to watch the trailer as I'm sold already! </p></div><div class="live-content"><time datetime="2026-06-02T21:47:17+00:00">June 2, 2026 – 5:47 PM</time><h2 id="marathon-season-2-takes-the-spotlight">Marathon Season 2 takes the spotlight</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/-WVhTcByDyY" allowfullscreen></iframe></div></div><p>Well, if you need a quick bathroom break, I guess this Marathon Season 2 trailer isn't a bad time for one. Seeing as the chat has been spamming "We Want Destiny 3" all day long, there are quite a few unhappy comments about it. Marathon Season 2 launches today with a free trial weekend, and the base game gets a discount.</p></div><div class="live-content"><time datetime="2026-06-02T21:49:26+00:00">June 2, 2026 – 5:49 PM</time><h2 id="runescape-is-arriving-on-ps5">Runescape is arriving on PS5</h2><p>My nostalgia levels are rising as for the first time a Runescape game is coming to PS5. Okay, it's not the base game but a spin-off, but still, Runescape on PS5! It's also be added to the PlayStation+ catalog on day one. </p><p>Sony's also confirming a new PlayStation Classics being added to PS+ including PSI-Ops, which has hit with another dose of childhood memories.</p></div><div class="live-content"><time datetime="2026-06-02T21:50:37+00:00">June 2, 2026 – 5:50 PM</time><h2 id="could-this-be-the-until-dawn-sequel">Could this be the Until Dawn Sequel?</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="swTtov7dnGkenzGkkFCfWA" name="until dawn trailer (3).jpg" alt="A screenshot showing characters from Until Dawn" src="https://cdn.mos.cms.futurecdn.net/swTtov7dnGkenzGkkFCfWA-1920-80.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: Sony / SuperMassive Games)</span></figcaption></figure><p>Rumors of an Until Dawn sequel have been ever-present for years, and this trailer that shows young adults having fun in paradise over sinister music is screaming Until Dawn to me! </p></div><div class="live-content"><time datetime="2026-06-02T21:52:15+00:00">June 2, 2026 – 5:52 PM</time><h2 id="yup-until-dawn-2-is-confirmed">Yup, Until Dawn 2 is confirmed</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/QpVZ9OvRLZI" allowfullscreen></iframe></div></div><p>And there you have it, in 2027 we are getting a follow-up to fan-favorite PS4 game Until Dawn 2. It's about a group of "fake" ghost hunters, who travel to a tropical island and find themselves trapped in a real nightmare. </p></div><div class="live-content"><time datetime="2026-06-02T21:53:36+00:00">June 2, 2026 – 5:53 PM</time><h2 id="the-final-reveal-will-be-20-minutes-long">The final reveal will be 20 minutes long!</h2><p>Wow, our last reveal of the night will be a "big" one, and it'll be 20 minutes of uninterrupted gameplay rather than merely a quick trailer or logo. Could it be the rumored God of War prequel starring Faye? </p><p>The Santa Monica Studio logo has appeared, I think it just might be...</p></div><div class="live-content"><time datetime="2026-06-02T21:56:25+00:00">June 2, 2026 – 5:56 PM</time><h2 id="that-s-god-of-war-alright">That's God of War alright!</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="ZjP4fqAEcPtL4Km8XsFF2d" name="state-of-play-june-2-god-of-war" alt="State of Play June 2026 screenshot" src="https://cdn.mos.cms.futurecdn.net/ZjP4fqAEcPtL4Km8XsFF2d-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>Yup, it looks like all the rumors were correct, our last reveal is a new God of War game, and it will be starring Faye. Who awakes <em>somewhere </em>after her death! </p></div><div class="live-content"><time datetime="2026-06-02T21:59:23+00:00">June 2, 2026 – 5:59 PM</time><h2 id="our-first-look-at-faye-in-action">Our first look at Faye in action!</h2><p>Whelp, she might not be Kratos, but Faye can certainly kick some butt. Our first look at this new God of War title (assuming it'll adopt the GoW name) sees Faye attacked by planet creatures and easily dispatch them with aggressive move set of her own </p></div><div class="live-content"><time datetime="2026-06-02T22:07:39+00:00">June 2, 2026 – 6:07 PM</time><h2 id="move-over-kratos-i-m-team-faye-now">Move over Kratos, I'm Team Faye now</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="X94e4UCURnQV3K4kkLeGgB" name="state-of-play-june-2-god-of-war-01" alt="state of play june 2 screenshot" src="https://cdn.mos.cms.futurecdn.net/X94e4UCURnQV3K4kkLeGgB-1920-80.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: Sony Interactive Entertainment)</span></figcaption></figure><p>I wasn't totally sold on a God of War prequel/spin-off without Kratos, but this gameplay is convincing me quickly. I'm wondering if PlayStation is showing us 20 minutes of gameplay out of the gate, could this game be closer to release? </p></div><div class="live-content"><time datetime="2026-06-02T22:15:47+00:00">June 2, 2026 – 6:15 PM</time><h2 id="and-it-s-called-god-of-war-laufey">And it's called God of War: Laufey</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/HLMX2w3cwuE" allowfullscreen></iframe></div></div><p>And there you have it, God of War: Laufey is coming to PS5. Not even a release year, so I guess my prediction that showing us so much of it today was a sign we might be close(ish) to release was wrong. I expect this is probably a late 2027 game at least.</p></div><div class="live-content"><time datetime="2026-06-02T22:16:54+00:00">June 2, 2026 – 6:16 PM</time><h2 id="and-that-s-the-state-of-play-june-2026-wrapped-up">And that's the State of Play June 2026 wrapped up!</h2><p>God of War: Laufey concludes today's State of Play. It was a suitably epic ending!</p></div><div class="live-content"><time datetime="2026-06-02T22:19:57+00:00">June 2, 2026 – 6:19 PM</time><h2 id="give-me-your-rating-for-today-s-state-of-play">Give me your rating for today's State of Play!</h2><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-O6jBVO"></div>                            </div>                            <script src="https://kwizly.com/embed/O6jBVO.js" async></script><p>Now that State of Play is over, I want to know what you make of the showcase from PlayStation. I'm going with a solid B+. It was a good showing, but lacked major surprises, and I'm quite disappointed we didn't see Intergalactic whatsoever.  </p></div><div class="live-content"><time datetime="2026-06-02T22:53:47+00:00">June 2, 2026 – 6:53 PM</time><h2 id="what-was-missing">What was missing?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="oSXnTeeWyDNn8CXYkWPAwN" name="resi-9-switch-2" alt="Resident Evil" src="https://cdn.mos.cms.futurecdn.net/oSXnTeeWyDNn8CXYkWPAwN-1920-80.png" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>We put up a poll earlier asking what games you were excited to see. </p><p>Unfortunately, every game on the list (except Wolverine) didn't make an appearance. </p><p>That includes Bloodborne (anything), FF7, RE9 DLC, Intergalactic, Horizon, Ken or Fairgames. It's actually surprising how many games were missing. </p><p>I did mention earlier that Summer Games Fest is on Friday. So, perhaps we'll see some of these games at that show, especially since they'll likely be multiplatform compared to some of the games shown today.</p></div><div class="live-content"><time datetime="2026-06-02T23:34:32+00:00">June 2, 2026 – 7:34 PM</time><h2 id="most-exciting-announcement">Most exciting announcement</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/nlt4o-mLxyM" allowfullscreen></iframe></div></div><p>Yes, God of War and Wolverine took up most of the but I think there were a couple of trailers that looked interesting.</p><p>For me, the Jurassic Park-esque "The Lost Wild" has my interest. I'm not sure where it's going, but I am curious.</p><p>Additionally, there was the trailer for Kemori. Look, I'm a surprisingly simple man, it turns out, and any game where you can grind wires like a skater will pique my interest.</p><p>There were more but I wanted to call those two out. Anything catch your eye?</p></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Watching the 2026 FIFA World Cup on your phone? I recommend these headphones to feel like you're actually at the games, starting at just $99 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>We've all been there. You're on a bus, train or other mode of public transportation, and you're consistently being disturbed by someone watching things on their phone — out loud, without using headphones or earbuds. When the <a href="https://www.tomsguide.com/entertainment/live/watch-world-cup-2026-free">2026 FIFA World Cup</a> starts in a couple of weeks, you'll see — hear, rather — people watching games out loud on their phones. Don't be that guy. No one likes that guy.</p><p>I, for one, know that I'll be using my phone to either catch up on games or watch them live, and there's an easy way to ensure you aren't disturbing anyone's peace, and that's by using a pair of the <a href="https://www.tomsguide.com/best-picks/best-over-ear-headphones">best headphones</a>. I test headphones day in and day out, so for your convenience, I've shortlisted my favorite cans — all of which will arrive well in time for the first  kick-off.</p><p>Whether you've got a budget of $100 or you're willing to spend nearly $500, there's a pair of headphones for everyone. The ones I've picked all feature highly effective ANC to make you feel immersed; clear vocals so that you can hear the commentary properly, and powerful bass so that you can feel the crowd rumbling and chanting in your chest.</p><h2 class="article-body__section" id="section-under-100"><span>Under $100</span></h2><div class="product"><a data-dimension112="f3c9989f-20d7-4df2-a33c-5c435468c3ed" data-action="Deal Block" data-label="Space One" data-dimension48="Space One" data-dimension25="$99" href="https://www.amazon.com/Soundcore-Cancelling-Headphones-Reduction-Comfortable/dp/B0C6KKQ7ND/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="JJByiZhukiUvSPjrhY8xY3" name="Anker Soundcore Space One" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/JJByiZhukiUvSPjrhY8xY3-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Though they have now been succeeded by the <a href="https://www.tomsguide.com/audio/headphones/soundcore-space-2-review" data-dimension112="f3c9989f-20d7-4df2-a33c-5c435468c3ed" data-action="Deal Block" data-label="Space One" data-dimension48="Space One" data-dimension25="$99">Space Two</a>, the OG <a href="https://www.tomsguide.com/reviews/anker-soundcore-space-one">Soundcore Space One</a> are an absolute bargain. Costing just $99, they're perfect for those looking for strong audio and ANC, as well as long battery life in a stylish package. Vocals especially sound crisp and invigorating, and even when the commentator yells "GOOOOOAL," rest assured that the high-pitch won't pierce your ears.<a class="view-deal button" href="https://www.amazon.com/Soundcore-Cancelling-Headphones-Reduction-Comfortable/dp/B0C6KKQ7ND/" target="_blank" rel="nofollow" data-dimension112="f3c9989f-20d7-4df2-a33c-5c435468c3ed" data-action="Deal Block" data-label="Space One" data-dimension48="Space One" data-dimension25="$99">View Deal</a></p></div><h2 class="article-body__section" id="section-under-200"><span>Under $200</span></h2><div class="product"><a data-dimension112="dbe23925-b9bc-4d50-b8dc-16b210ace5ba" data-action="Deal Block" data-label="Epic Lux Lab Edition" data-dimension48="Epic Lux Lab Edition" data-dimension25="$199" href="https://www.amazon.com/JLab-Epic-Wireless-Headphones-Graphite/dp/B0DRDNJNTD/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:943px;"><p class="vanilla-image-block" style="padding-top:159.07%;"><img id="naab9kQoe9jnyUz73sJU8J" name="Jlab-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/naab9kQoe9jnyUz73sJU8J-1920-80.jpg" mos="" align="middle" fullscreen="" width="943" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Bass is the <a href="https://www.tomsguide.com/audio/over-ear-headphones/jlab-epic-lux-lab-edition-review" data-dimension112="dbe23925-b9bc-4d50-b8dc-16b210ace5ba" data-action="Deal Block" data-label="Epic Lux Lab Edition" data-dimension48="Epic Lux Lab Edition" data-dimension25="$199">JLab Epic Lux Lab Edition</a>'s speciality, so you can thoroughly enjoy the deep rumbling of the stadium and the emotion in the commentators' voices. Plenty of detail in vocals and especially in the mid-range brings commentary and stadium atmosphere to life.<a class="view-deal button" href="https://www.amazon.com/JLab-Epic-Wireless-Headphones-Graphite/dp/B0DRDNJNTD/" target="_blank" rel="nofollow" data-dimension112="dbe23925-b9bc-4d50-b8dc-16b210ace5ba" data-action="Deal Block" data-label="Epic Lux Lab Edition" data-dimension48="Epic Lux Lab Edition" data-dimension25="$199">View Deal</a></p></div><h2 class="article-body__section" id="section-under-300"><span>Under $300</span></h2><div class="product"><a data-dimension112="d91824b8-98ea-4b27-a032-54a6d74482cb" data-action="Deal Block" data-label="Monitor III" data-dimension48="Monitor III" data-dimension25="$248" href="https://www.amazon.com/Marshall-Canceling-Over-Ear-Bluetooth-Headphones/dp/B0DFDT1TWC/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="aQySZAydrEzPDCrumqsYBR" name="Marshall Monitor III" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/aQySZAydrEzPDCrumqsYBR-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you want long-lasting headphones, few (if any) are better than the <a href="https://www.tomsguide.com/audio/headphones/marshall-monitor-iii-review" data-dimension112="d91824b8-98ea-4b27-a032-54a6d74482cb" data-action="Deal Block" data-label="Monitor III" data-dimension48="Monitor III" data-dimension25="$248">Marshall Monitor III</a>. With up to 70 hours of battery life with ANC and 100 hours without, you can stream multiple games, highlights, fan compilations, and everything in between for days without recharging. And of course, you can still enjoy powerful ANC and immersive sound.<a class="view-deal button" href="https://www.amazon.com/Marshall-Canceling-Over-Ear-Bluetooth-Headphones/dp/B0DFDT1TWC/" target="_blank" rel="nofollow" data-dimension112="d91824b8-98ea-4b27-a032-54a6d74482cb" data-action="Deal Block" data-label="Monitor III" data-dimension48="Monitor III" data-dimension25="$248">View Deal</a></p></div><div class="product"><a data-dimension112="c3e21a03-fdc6-4de4-a225-4c0e661d4707" data-action="Deal Block" data-label="Milton ANC" data-dimension48="Milton ANC" data-dimension25="$249" href="https://www.amazon.com/Marshall-Wireless-Headphones-Adaptive-Cancelling/dp/B0GLSPPZJ4/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3915px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="W7JiRjcJKxDsmnvxb5hcBR" name="Marshall-Milton-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/W7JiRjcJKxDsmnvxb5hcBR-1920-80.jpg" mos="" align="middle" fullscreen="" width="3915" height="3915" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you aren't opposed to on-ear cans, the <a href="https://www.tomsguide.com/audio/headphones/marshall-milton-anc-review" data-dimension112="c3e21a03-fdc6-4de4-a225-4c0e661d4707" data-action="Deal Block" data-label="Milton ANC" data-dimension48="Milton ANC" data-dimension25="$249">Marshall Milton ANC</a> are an outstanding choice. One of their biggest selling points is Soundstage spatial audio which moves the sound into a virtual room (whose size can be customized) and makes you feel like you're at the heart of the action. Even if you didn't get tickets to the games, you can (sort of) recreate that feeling.<a class="view-deal button" href="https://www.amazon.com/Marshall-Wireless-Headphones-Adaptive-Cancelling/dp/B0GLSPPZJ4/" target="_blank" rel="nofollow" data-dimension112="c3e21a03-fdc6-4de4-a225-4c0e661d4707" data-action="Deal Block" data-label="Milton ANC" data-dimension48="Milton ANC" data-dimension25="$249">View Deal</a></p></div><h2 class="article-body__section" id="section-under-400"><span>Under $400</span></h2><div class="product"><a data-dimension112="3b36dc6f-e11f-4047-995c-71f92f167de7" data-action="Deal Block" data-label="WH-1000XM6" data-dimension48="WH-1000XM6" data-dimension25="$398" href="https://www.amazon.com/Sony-WH-1000XM6-Headphones-Microphones-Studio-Quality/dp/B0F3PQHWTZ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="x3hpHUb8bPXUJsBkoevKDN" name="Sony WH-1000XM6" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/x3hpHUb8bPXUJsBkoevKDN-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review" data-dimension112="3b36dc6f-e11f-4047-995c-71f92f167de7" data-action="Deal Block" data-label="WH-1000XM6" data-dimension48="WH-1000XM6" data-dimension25="$398">Sony WH-1000XM6</a> feature excellent noise cancellation — though not as good as Bose's, it's still solid, and they'll help keep your immersion intact. These cans are second to none when it comes to vocal clarity, so you can enjoy every small detail in the commentators' and crowd's voices. You might be able to understand what a mic-less player has shouted across the pitch, too.<a class="view-deal button" href="https://www.amazon.com/Sony-WH-1000XM6-Headphones-Microphones-Studio-Quality/dp/B0F3PQHWTZ/" target="_blank" rel="nofollow" data-dimension112="3b36dc6f-e11f-4047-995c-71f92f167de7" data-action="Deal Block" data-label="WH-1000XM6" data-dimension48="WH-1000XM6" data-dimension25="$398">View Deal</a></p></div><h2 class="article-body__section" id="section-under-500"><span>Under $500</span></h2><div class="product"><a data-dimension112="49ebaa2a-14c6-4b1d-ac15-b84897392201" data-action="Deal Block" data-label="QuietComfort Ultra 2nd Gen" data-dimension48="QuietComfort Ultra 2nd Gen" data-dimension25="$449" href="https://www.amazon.com/Bose-QuietComfort-Bluetooth-Headphones-Cancelling/dp/B0FDKR293G/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1200px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="iPfpWwrQeGYvMmxuZPCecS" name="Bose QC Ultra gen 2 deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/iPfpWwrQeGYvMmxuZPCecS-1920-80.jpg" mos="" align="middle" fullscreen="" width="1200" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-QuietComfort-Bluetooth-Headphones-Cancelling/dp/B0FDKR293G/" target="_blank" rel="nofollow" data-dimension112="49ebaa2a-14c6-4b1d-ac15-b84897392201" data-action="Deal Block" data-label="QuietComfort Ultra 2nd Gen" data-dimension48="QuietComfort Ultra 2nd Gen" data-dimension25="$449">QuietComfort Ultra 2nd Gen: $449</a></strong><br>No one does ANC better than Bose, and the <a href="https://www.tomsguide.com/audio/over-ear-headphones/bose-quietcomfort-ultra-headphones-2nd-generation-review">Bose QuietComfort Ultra Gen 2</a> are the perfect headphones for immersive listening as you can't hear the outside world. Rich spatial audio further bolsters your immersion, and the powerful bass amplifies every chant and commentary moment. The comfortable fit makes the QC Ultra Gen 2 perfect for extended or back-to-back games.<a class="view-deal button" href="https://www.amazon.com/Bose-QuietComfort-Bluetooth-Headphones-Cancelling/dp/B0FDKR293G/" target="_blank" rel="nofollow" data-dimension112="49ebaa2a-14c6-4b1d-ac15-b84897392201" data-action="Deal Block" data-label="QuietComfort Ultra 2nd Gen" data-dimension48="QuietComfort Ultra 2nd Gen" data-dimension25="$449">View Deal</a></p></div><h2 class="article-body__section" id="section-under-600"><span>Under $600</span></h2><div class="product"><a data-dimension112="a06d5552-ecb5-4f71-9cba-8a24385be1ab" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$509" href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS4SGZR/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="TSi2XMUrFhYn5nqrGBsd59" name="airpods max 2 deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/TSi2XMUrFhYn5nqrGBsd59-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review" data-dimension112="a06d5552-ecb5-4f71-9cba-8a24385be1ab" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$509">Apple AirPods Max 2</a> make listening to music and watching a game extremely fun, thanks to their energetic sound profile. With personalized spatial audio and incredible ANC, the headphones capture and reproduce the energy of the stadium. If you're already deep into Apple's ecosystem, it makes sense to get the AirPods Max 2 for seamless swapping between devices.<a class="view-deal button" href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS4SGZR/" target="_blank" rel="nofollow" data-dimension112="a06d5552-ecb5-4f71-9cba-8a24385be1ab" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$509">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Ww1noX"></div>                            </div>                            <script src="https://kwizly.com/embed/Ww1noX.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/audio/over-ear-headphones/the-apple-airpods-max-2-are-actually-great-headphones">I thought the Apple AirPods Max 2 were way overhyped — but I finally tried them and they're some of the best headphones I've ever used</a></li><li><a href="https://www.tomsguide.com/audio/headphones/marshall-milton-anc-review">I've been testing the new Marshall Milton ANC for 3 weeks — they fix my biggest gripe with on-ear headphones, and are the new gold standard</a></li><li><a href="https://www.tomsguide.com/audio/headphones/my-doctor-banned-me-from-earbuds-so-i-got-these-marshall-headphones-instead-and-im-surprised-i-didnt-do-it-sooner">My doctor banned me from earbuds so I got these Marshall headphones instead — and I’m surprised I didn’t do it sooner</a></li></ul><div data-widget-type="multimodelreview" data-widget-title="Today's best headphones deals" data-model-name="Anker Soundcore Space One,JLab Epic Lux Lab Edition,Marshall Milton ANC,Marshall Monitor III ANC,Sony WH-1000XM6,Bose QuietComfort Ultra Headphones (2nd Gen),Apple AirPods Max 2"></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/headphones/headphones-for-fifa-world-cup-2026</link>
                                                                            <description>
                            <![CDATA[ If you're catching up on highlights or watching the match in public, these are my top headphone picks for you. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">cZfxaVBE5eMTzkjWgJgfxZ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/eSCGe4SAPSngvN54YSgm7Y-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 01 Jun 2026 09:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Headphones]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3-320-70.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&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/eSCGe4SAPSngvN54YSgm7Y-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Bose QuietComfort Ultra 2nd Gen, Marshall Milton ANC and Sony WH-1000XM6 headphones]]></media:description>                                                            <media:text><![CDATA[Bose QuietComfort Ultra 2nd Gen, Marshall Milton ANC and Sony WH-1000XM6 headphones]]></media:text>
                                <media:title type="plain"><![CDATA[Bose QuietComfort Ultra 2nd Gen, Marshall Milton ANC and Sony WH-1000XM6 headphones]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/eSCGe4SAPSngvN54YSgm7Y-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>We've all been there. You're on a bus, train or other mode of public transportation, and you're consistently being disturbed by someone watching things on their phone — out loud, without using headphones or earbuds. When the <a href="https://www.tomsguide.com/entertainment/live/watch-world-cup-2026-free">2026 FIFA World Cup</a> starts in a couple of weeks, you'll see — hear, rather — people watching games out loud on their phones. Don't be that guy. No one likes that guy.</p><p>I, for one, know that I'll be using my phone to either catch up on games or watch them live, and there's an easy way to ensure you aren't disturbing anyone's peace, and that's by using a pair of the <a href="https://www.tomsguide.com/best-picks/best-over-ear-headphones">best headphones</a>. I test headphones day in and day out, so for your convenience, I've shortlisted my favorite cans — all of which will arrive well in time for the first  kick-off.</p><p>Whether you've got a budget of $100 or you're willing to spend nearly $500, there's a pair of headphones for everyone. The ones I've picked all feature highly effective ANC to make you feel immersed; clear vocals so that you can hear the commentary properly, and powerful bass so that you can feel the crowd rumbling and chanting in your chest.</p><h2 class="article-body__section" id="section-under-100"><span>Under $100</span></h2><div class="product"><a data-dimension112="f3c9989f-20d7-4df2-a33c-5c435468c3ed" data-action="Deal Block" data-label="Space One" data-dimension48="Space One" data-dimension25="$99" href="https://www.amazon.com/Soundcore-Cancelling-Headphones-Reduction-Comfortable/dp/B0C6KKQ7ND/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="JJByiZhukiUvSPjrhY8xY3" name="Anker Soundcore Space One" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/JJByiZhukiUvSPjrhY8xY3-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Though they have now been succeeded by the <a href="https://www.tomsguide.com/audio/headphones/soundcore-space-2-review" data-dimension112="f3c9989f-20d7-4df2-a33c-5c435468c3ed" data-action="Deal Block" data-label="Space One" data-dimension48="Space One" data-dimension25="$99">Space Two</a>, the OG <a href="https://www.tomsguide.com/reviews/anker-soundcore-space-one">Soundcore Space One</a> are an absolute bargain. Costing just $99, they're perfect for those looking for strong audio and ANC, as well as long battery life in a stylish package. Vocals especially sound crisp and invigorating, and even when the commentator yells "GOOOOOAL," rest assured that the high-pitch won't pierce your ears.<a class="view-deal button" href="https://www.amazon.com/Soundcore-Cancelling-Headphones-Reduction-Comfortable/dp/B0C6KKQ7ND/" target="_blank" rel="nofollow" data-dimension112="f3c9989f-20d7-4df2-a33c-5c435468c3ed" data-action="Deal Block" data-label="Space One" data-dimension48="Space One" data-dimension25="$99">View Deal</a></p></div><h2 class="article-body__section" id="section-under-200"><span>Under $200</span></h2><div class="product"><a data-dimension112="dbe23925-b9bc-4d50-b8dc-16b210ace5ba" data-action="Deal Block" data-label="Epic Lux Lab Edition" data-dimension48="Epic Lux Lab Edition" data-dimension25="$199" href="https://www.amazon.com/JLab-Epic-Wireless-Headphones-Graphite/dp/B0DRDNJNTD/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:943px;"><p class="vanilla-image-block" style="padding-top:159.07%;"><img id="naab9kQoe9jnyUz73sJU8J" name="Jlab-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/naab9kQoe9jnyUz73sJU8J-1920-80.jpg" mos="" align="middle" fullscreen="" width="943" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Bass is the <a href="https://www.tomsguide.com/audio/over-ear-headphones/jlab-epic-lux-lab-edition-review" data-dimension112="dbe23925-b9bc-4d50-b8dc-16b210ace5ba" data-action="Deal Block" data-label="Epic Lux Lab Edition" data-dimension48="Epic Lux Lab Edition" data-dimension25="$199">JLab Epic Lux Lab Edition</a>'s speciality, so you can thoroughly enjoy the deep rumbling of the stadium and the emotion in the commentators' voices. Plenty of detail in vocals and especially in the mid-range brings commentary and stadium atmosphere to life.<a class="view-deal button" href="https://www.amazon.com/JLab-Epic-Wireless-Headphones-Graphite/dp/B0DRDNJNTD/" target="_blank" rel="nofollow" data-dimension112="dbe23925-b9bc-4d50-b8dc-16b210ace5ba" data-action="Deal Block" data-label="Epic Lux Lab Edition" data-dimension48="Epic Lux Lab Edition" data-dimension25="$199">View Deal</a></p></div><h2 class="article-body__section" id="section-under-300"><span>Under $300</span></h2><div class="product"><a data-dimension112="d91824b8-98ea-4b27-a032-54a6d74482cb" data-action="Deal Block" data-label="Monitor III" data-dimension48="Monitor III" data-dimension25="$248" href="https://www.amazon.com/Marshall-Canceling-Over-Ear-Bluetooth-Headphones/dp/B0DFDT1TWC/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="aQySZAydrEzPDCrumqsYBR" name="Marshall Monitor III" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/aQySZAydrEzPDCrumqsYBR-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you want long-lasting headphones, few (if any) are better than the <a href="https://www.tomsguide.com/audio/headphones/marshall-monitor-iii-review" data-dimension112="d91824b8-98ea-4b27-a032-54a6d74482cb" data-action="Deal Block" data-label="Monitor III" data-dimension48="Monitor III" data-dimension25="$248">Marshall Monitor III</a>. With up to 70 hours of battery life with ANC and 100 hours without, you can stream multiple games, highlights, fan compilations, and everything in between for days without recharging. And of course, you can still enjoy powerful ANC and immersive sound.<a class="view-deal button" href="https://www.amazon.com/Marshall-Canceling-Over-Ear-Bluetooth-Headphones/dp/B0DFDT1TWC/" target="_blank" rel="nofollow" data-dimension112="d91824b8-98ea-4b27-a032-54a6d74482cb" data-action="Deal Block" data-label="Monitor III" data-dimension48="Monitor III" data-dimension25="$248">View Deal</a></p></div><div class="product"><a data-dimension112="c3e21a03-fdc6-4de4-a225-4c0e661d4707" data-action="Deal Block" data-label="Milton ANC" data-dimension48="Milton ANC" data-dimension25="$249" href="https://www.amazon.com/Marshall-Wireless-Headphones-Adaptive-Cancelling/dp/B0GLSPPZJ4/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3915px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="W7JiRjcJKxDsmnvxb5hcBR" name="Marshall-Milton-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/W7JiRjcJKxDsmnvxb5hcBR-1920-80.jpg" mos="" align="middle" fullscreen="" width="3915" height="3915" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you aren't opposed to on-ear cans, the <a href="https://www.tomsguide.com/audio/headphones/marshall-milton-anc-review" data-dimension112="c3e21a03-fdc6-4de4-a225-4c0e661d4707" data-action="Deal Block" data-label="Milton ANC" data-dimension48="Milton ANC" data-dimension25="$249">Marshall Milton ANC</a> are an outstanding choice. One of their biggest selling points is Soundstage spatial audio which moves the sound into a virtual room (whose size can be customized) and makes you feel like you're at the heart of the action. Even if you didn't get tickets to the games, you can (sort of) recreate that feeling.<a class="view-deal button" href="https://www.amazon.com/Marshall-Wireless-Headphones-Adaptive-Cancelling/dp/B0GLSPPZJ4/" target="_blank" rel="nofollow" data-dimension112="c3e21a03-fdc6-4de4-a225-4c0e661d4707" data-action="Deal Block" data-label="Milton ANC" data-dimension48="Milton ANC" data-dimension25="$249">View Deal</a></p></div><h2 class="article-body__section" id="section-under-400"><span>Under $400</span></h2><div class="product"><a data-dimension112="3b36dc6f-e11f-4047-995c-71f92f167de7" data-action="Deal Block" data-label="WH-1000XM6" data-dimension48="WH-1000XM6" data-dimension25="$398" href="https://www.amazon.com/Sony-WH-1000XM6-Headphones-Microphones-Studio-Quality/dp/B0F3PQHWTZ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="x3hpHUb8bPXUJsBkoevKDN" name="Sony WH-1000XM6" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/x3hpHUb8bPXUJsBkoevKDN-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review" data-dimension112="3b36dc6f-e11f-4047-995c-71f92f167de7" data-action="Deal Block" data-label="WH-1000XM6" data-dimension48="WH-1000XM6" data-dimension25="$398">Sony WH-1000XM6</a> feature excellent noise cancellation — though not as good as Bose's, it's still solid, and they'll help keep your immersion intact. These cans are second to none when it comes to vocal clarity, so you can enjoy every small detail in the commentators' and crowd's voices. You might be able to understand what a mic-less player has shouted across the pitch, too.<a class="view-deal button" href="https://www.amazon.com/Sony-WH-1000XM6-Headphones-Microphones-Studio-Quality/dp/B0F3PQHWTZ/" target="_blank" rel="nofollow" data-dimension112="3b36dc6f-e11f-4047-995c-71f92f167de7" data-action="Deal Block" data-label="WH-1000XM6" data-dimension48="WH-1000XM6" data-dimension25="$398">View Deal</a></p></div><h2 class="article-body__section" id="section-under-500"><span>Under $500</span></h2><div class="product"><a data-dimension112="49ebaa2a-14c6-4b1d-ac15-b84897392201" data-action="Deal Block" data-label="QuietComfort Ultra 2nd Gen" data-dimension48="QuietComfort Ultra 2nd Gen" data-dimension25="$449" href="https://www.amazon.com/Bose-QuietComfort-Bluetooth-Headphones-Cancelling/dp/B0FDKR293G/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1200px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="iPfpWwrQeGYvMmxuZPCecS" name="Bose QC Ultra gen 2 deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/iPfpWwrQeGYvMmxuZPCecS-1920-80.jpg" mos="" align="middle" fullscreen="" width="1200" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-QuietComfort-Bluetooth-Headphones-Cancelling/dp/B0FDKR293G/" target="_blank" rel="nofollow" data-dimension112="49ebaa2a-14c6-4b1d-ac15-b84897392201" data-action="Deal Block" data-label="QuietComfort Ultra 2nd Gen" data-dimension48="QuietComfort Ultra 2nd Gen" data-dimension25="$449">QuietComfort Ultra 2nd Gen: $449</a></strong><br>No one does ANC better than Bose, and the <a href="https://www.tomsguide.com/audio/over-ear-headphones/bose-quietcomfort-ultra-headphones-2nd-generation-review">Bose QuietComfort Ultra Gen 2</a> are the perfect headphones for immersive listening as you can't hear the outside world. Rich spatial audio further bolsters your immersion, and the powerful bass amplifies every chant and commentary moment. The comfortable fit makes the QC Ultra Gen 2 perfect for extended or back-to-back games.<a class="view-deal button" href="https://www.amazon.com/Bose-QuietComfort-Bluetooth-Headphones-Cancelling/dp/B0FDKR293G/" target="_blank" rel="nofollow" data-dimension112="49ebaa2a-14c6-4b1d-ac15-b84897392201" data-action="Deal Block" data-label="QuietComfort Ultra 2nd Gen" data-dimension48="QuietComfort Ultra 2nd Gen" data-dimension25="$449">View Deal</a></p></div><h2 class="article-body__section" id="section-under-600"><span>Under $600</span></h2><div class="product"><a data-dimension112="a06d5552-ecb5-4f71-9cba-8a24385be1ab" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$509" href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS4SGZR/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="TSi2XMUrFhYn5nqrGBsd59" name="airpods max 2 deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/TSi2XMUrFhYn5nqrGBsd59-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review" data-dimension112="a06d5552-ecb5-4f71-9cba-8a24385be1ab" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$509">Apple AirPods Max 2</a> make listening to music and watching a game extremely fun, thanks to their energetic sound profile. With personalized spatial audio and incredible ANC, the headphones capture and reproduce the energy of the stadium. If you're already deep into Apple's ecosystem, it makes sense to get the AirPods Max 2 for seamless swapping between devices.<a class="view-deal button" href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS4SGZR/" target="_blank" rel="nofollow" data-dimension112="a06d5552-ecb5-4f71-9cba-8a24385be1ab" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$509">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Ww1noX"></div>                            </div>                            <script src="https://kwizly.com/embed/Ww1noX.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/audio/over-ear-headphones/the-apple-airpods-max-2-are-actually-great-headphones">I thought the Apple AirPods Max 2 were way overhyped — but I finally tried them and they're some of the best headphones I've ever used</a></li><li><a href="https://www.tomsguide.com/audio/headphones/marshall-milton-anc-review">I've been testing the new Marshall Milton ANC for 3 weeks — they fix my biggest gripe with on-ear headphones, and are the new gold standard</a></li><li><a href="https://www.tomsguide.com/audio/headphones/my-doctor-banned-me-from-earbuds-so-i-got-these-marshall-headphones-instead-and-im-surprised-i-didnt-do-it-sooner">My doctor banned me from earbuds so I got these Marshall headphones instead — and I’m surprised I didn’t do it sooner</a></li></ul><div data-widget-type="multimodelreview" data-widget-title="Today's best headphones deals" data-model-name="Anker Soundcore Space One,JLab Epic Lux Lab Edition,Marshall Milton ANC,Marshall Monitor III ANC,Sony WH-1000XM6,Bose QuietComfort Ultra Headphones (2nd Gen),Apple AirPods Max 2"></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ We just tested the Bravia 7 II — here’s why I think it could be the Sony TV to buy in 2026 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Sony — maker of some of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> in the world — just now unveiled its 2026 TV lineup, and while it’s later to the party than LG, Samsung, Hisense and TCL, it’s nevertheless making a grand entrance.</p><p>This week, the brand officially announced the <a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs">Bravia 9 II</a> and Bravia 7 II, a pair of follow-ups to the <a href="https://www.tomsguide.com/tvs/sony-bravia-9-tv-review">Bravia 9</a> and <a href="https://www.tomsguide.com/tvs/sony-bravia-7-tv-review">Bravia 7</a> Mini-LED TVs. The originals were among the best-performing Mini-LED TVs in their class, but their successors — which carry the <em>mark two </em>designation — come with Sony’s all-new, True RGB display technology.</p><p>Needless to say, the competitive stakes are high. Sony’s rivals have showcased their own spin on RGB LED TVs, too, and pricing on these models have been relatively high. But we were lucky enough to get our hands on the Bravia 7 II for testing, and after poring over the results, it could be my dark horse candidate for the best RGB LED TV for most people in 2026. </p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/J3z1ok-smVc" allowfullscreen></iframe></div></div><h2 id="sony-bravia-7-ii-test-results">Sony Bravia 7 II test results</h2><p>While we’re still cracking away at our full review, here’s a sneak-peek at how the Bravia 7 II managed in our test lab. For context, I’ve included the test results of both the Bravia 9 II (Sony’s latest flagship True RGB TV) as well as the Sony Bravia 9 (the brand’s previous Mini-LED flagship).</p><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Sony Bravia 7 II</strong></p></td><td  ><p><strong>Sony Bravia 9 II</strong></p></td><td  ><p><strong>Sony Bravia 9</strong></p></td></tr><tr><td class="firstcol " ><p>HDR Brightness (10%, in nits)</p></td><td  ><p>2,078</p></td><td  ><p>3,990</p></td><td  ><p>2,713</p></td></tr><tr><td class="firstcol " ><p>Rec. 2020 Gamut Coverage</p></td><td  ><p>87.2%</p></td><td  ><p>87.3%</p></td><td  ><p>78.02%</p></td></tr></tbody></table></div><p>As far as HDR highlight brightness goes, the Bravia 7 II is just under what we measured on the Bravia 9 a few years ago. This is plenty bright for most viewing environments, and if Sony’s picture processing delivers a similar HDR presentation as the flagship Bravia 9, I’ll be thrilled.</p><div><blockquote><p>If Sony’s picture processing delivers a similar HDR presentation as the flagship Bravia 9, I’ll be thrilled.</p></blockquote></div><p>HDR brightness is shaping up to be one of the primary distinctions between the Bravia 7 II and the all-new Bravia 9 II. If you’re in the market for a Sony-engineered set and you’re chasing the brightest possible picture, you’ll have to splash out on the Bravia 9 II.</p><p>However, when it comes to overall color volume, the Bravia 7 II is right in line with its souped-up, RGB LED-powered sibling. Given that color is the bread and butter of RGB LED technology, it’s heartening to learn that Sony’s entry-level RGB LED TV is cooking with a similar set of ingredients as the brand’s top-shelf model.</p><p>And, while I’ll reserve my full judgment for after I’ve had a chance to spend some time with these new TVs, when I look at these test results, I can’t help but see the Bravia 7 II as a slightly stripped-down version of the original Bravia 9. This set was (and still is) one of my favorite Mini-LED TVs ever made, and while the Bravia 7 II isn’t quite as bright, it gets pretty close — all while delivering punchier-looking color.</p><h2 id="sony-bravia-7-ii-vs-bravia-9-ii-pricing-and-availability">Sony Bravia 7 II vs Bravia 9 II: pricing and 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="KGZxKdWq8FXSaaUB6WyWWA" name="Sony-Bravia-9-II-85-inch" alt="Lifestyle image of the Sony Bravia 9 II (85-inch model) wall mounted in a living room environment." src="https://cdn.mos.cms.futurecdn.net/KGZxKdWq8FXSaaUB6WyWWA-1920-80.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: Sony)</span></figcaption></figure><p>So, let’s say that you decide to go with the Bravia 7 II rather than springing for the flagship-level Bravia 9 II. How much money, exactly, are you saving?</p><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Sony Bravia 9 II</strong></p></td><td  ><p><strong>Sony Bravia 7 II</strong></p></td></tr><tr><td class="firstcol " ><p><strong>50 inch</strong></p></td><td  ><p>N/A</p></td><td  ><p>$1,599.99 </p></td></tr><tr><td class="firstcol " ><p><strong>55 inch</strong></p></td><td  ><p>N/A</p></td><td  ><p>$2,099.99</p></td></tr><tr><td class="firstcol " ><p><strong>65 inch</strong></p></td><td  ><p> $3,599.99</p></td><td  ><p>$2,599.99</p></td></tr><tr><td class="firstcol " ><p><strong>75 inch</strong></p></td><td  ><p>$4,599.99</p></td><td  ><p>$3,099.99 </p></td></tr><tr><td class="firstcol " ><p><strong>85 inch</strong></p></td><td  ><p>$6,499.99</p></td><td  ><p>$3,999.99</p></td></tr><tr><td class="firstcol " ><p><strong>98 inch</strong></p></td><td  ><p>N/A</p></td><td  ><p>$8,999.99</p></td></tr><tr><td class="firstcol " ><p><strong>115 inch</strong></p></td><td  ><p>$30,999.99</p></td><td  ><p>N/A</p></td></tr></tbody></table></div><p>Of the two, the Bravia 7 II is the only one available in sizes below 65 inches. If you’re looking for a 50- or 55-inch TV, the Bravia 7 II is your sole option.</p><p>But if you’re choosing between the two at the 65-, 75- or 85-inch level, you stand to save between $1,000 and $2,500 by going with the Bravia 7 II. Make no mistake: The Bravia 7 II’s pricing is on par with the MSRP of mid-range OLED TVs (like the <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled-review">LG C6</a>), but you’re saving a significant amount of cash by choosing the Bravia 7 II over the Bravia 9 II.</p><p>Compared to the original Bravia 9 (now a few years old), the Bravia 7 II is a bit pricier at launch. Right now, you can get the<a href="https://www.bestbuy.com/product/sony-65-class-bravia-9-mini-led-qled-4k-uhd-smart-google-tv-2024/J7XSRH59ZQ/sku/6578568?utm_source=feed&extStoreId=596&ref=212&loc=18467156568&gclsrc=aw.ds&gad_source=1&gad_campaignid=18470468791&gbraid=0AAAAAD-ORIhiR0dnNxsHa44B7IUDnVUDD&gclid=Cj0KCQjwz9_QBhD_ARIsADnSCfDkqKPCHm8j7c2n6GDy72LT-F8m8_ifi7AKHlWyUhkzthnbUcQSKkEaAjCkEALw_wcB"> 65-inch Sony Bravia 9 for $2,099 at Best Buy</a>. This is much more affordable than it was when it made its debut in 2024, but it’s $500 more than the Bravia 7 II, and the same price as the 55-inch Bravia 7 II.</p><h2 id="sony-bravia-7-ii-outlook-2">Sony Bravia 7 II: outlook</h2><p>As mentioned, we’re still hard at work on our full reviews of both the Bravia 7 II and the Bravia 9 II, so we’re still evaluating other aspects of these TVs’ performance. Until the dust settles, I’m hesitant to make any formal declarations about which TV is a better fit for various viewers.</p><p>That said, while the Bravia 9 II is shaping up to be the crown jewel of Sony’s 2026 TV lineup, I must admit that I am mighty excited to spend some quality time with the Bravia 7 II, too. Given Sony’s track record for delivering finely tuned, detail-oriented TVs with top-notch picture processing, the Bravia 7 II might prove to be the all-around best option for people who are curious about RGB LED technology, but who’d rather not shell out for a first-generation flagship.</p><div><blockquote><p>The Bravia 7 II might prove to be the all-around best option for people who are curious about RGB LED technology, but who’d rather not shell out for a first-generation flagship.</p></blockquote></div><p>Of course, Sony will be up against a number of entry-level RGB LED TVs that offer a similar compromise, including the Samsung R85H and the Hisense UR8. How it fares against those sets remains to be seen.</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/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs#section-sony-bravia-9-ii-bravia-7-ii-price-and-availability">I saw Sony's Bravia 9 II up close and it completely shattered my expectations — here’s why it’s the king of RGB TVs</a></li><li><a href="https://www.tomsguide.com/tvs/samsungs-new-micro-rgb-tv-is-finally-here-for-usd3-200-but-id-rather-buy-this-oled-instead">Samsung's new Micro RGB TV is finally here — but I'd rather buy this OLED instead</a></li><li><a href="https://www.tomsguide.com/tvs/3-reasons-why-you-shouldnt-buy-an-rgb-led-tv-in-2026-and-1-reason-you-should">3 reasons why you shouldn't buy an RGB LED TV in 2026</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/we-just-tested-the-bravia-7-ii-heres-why-i-think-it-could-be-the-sony-tv-to-buy-in-2026</link>
                                                                            <description>
                            <![CDATA[ This week, Sony officially announced the Bravia 9 II and the Bravia 7 II, a pair of True RGB Mini-LED TVs. We've already tested the Bravia 7 II, and from the looks of our test results, it could be the best entry-level RGB LED TV of 2026. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">eZoDei7znNP8HpdTnUQnLL</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/hRsaSUENiEU9eBTastd9Jg-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 29 May 2026 11:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[TVs]]></category>
                                                                                                <author><![CDATA[ michael.desjardin@futurenet.com (Michael Desjardin) ]]></author>                    <dc:creator><![CDATA[ Michael Desjardin ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/esytxRqW9GNVZNVCf7BFyV-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael Desjardin is a Senior Editor for TVs at Tom&#039;s Guide. He&#039;s been testing and tinkering with TVs professionally for over a decade, previously for Reviewed and USA Today. Michael studied media production and screenwriting at Emerson College. He loves cooking, zoning out to ambient music, and getting way too invested in the Red Sox. He considers himself living proof that TV doesn&#039;t necessarily rot your brain.&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/hRsaSUENiEU9eBTastd9Jg-1920-80.jpg">
                                                            <media:credit><![CDATA[Sony]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A wall-mounted Sony Bravia 7 II True RGB Mini-LED TV displaying a colorful, abstract image in a modern living room]]></media:description>                                                            <media:text><![CDATA[A wall-mounted Sony Bravia 7 II True RGB Mini-LED TV displaying a colorful, abstract image in a modern living room]]></media:text>
                                <media:title type="plain"><![CDATA[A wall-mounted Sony Bravia 7 II True RGB Mini-LED TV displaying a colorful, abstract image in a modern living room]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/hRsaSUENiEU9eBTastd9Jg-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Sony — maker of some of the <a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">best TVs</a> in the world — just now unveiled its 2026 TV lineup, and while it’s later to the party than LG, Samsung, Hisense and TCL, it’s nevertheless making a grand entrance.</p><p>This week, the brand officially announced the <a href="https://www.tomsguide.com/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs">Bravia 9 II</a> and Bravia 7 II, a pair of follow-ups to the <a href="https://www.tomsguide.com/tvs/sony-bravia-9-tv-review">Bravia 9</a> and <a href="https://www.tomsguide.com/tvs/sony-bravia-7-tv-review">Bravia 7</a> Mini-LED TVs. The originals were among the best-performing Mini-LED TVs in their class, but their successors — which carry the <em>mark two </em>designation — come with Sony’s all-new, True RGB display technology.</p><p>Needless to say, the competitive stakes are high. Sony’s rivals have showcased their own spin on RGB LED TVs, too, and pricing on these models have been relatively high. But we were lucky enough to get our hands on the Bravia 7 II for testing, and after poring over the results, it could be my dark horse candidate for the best RGB LED TV for most people in 2026. </p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/J3z1ok-smVc" allowfullscreen></iframe></div></div><h2 id="sony-bravia-7-ii-test-results">Sony Bravia 7 II test results</h2><p>While we’re still cracking away at our full review, here’s a sneak-peek at how the Bravia 7 II managed in our test lab. For context, I’ve included the test results of both the Bravia 9 II (Sony’s latest flagship True RGB TV) as well as the Sony Bravia 9 (the brand’s previous Mini-LED flagship).</p><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Sony Bravia 7 II</strong></p></td><td  ><p><strong>Sony Bravia 9 II</strong></p></td><td  ><p><strong>Sony Bravia 9</strong></p></td></tr><tr><td class="firstcol " ><p>HDR Brightness (10%, in nits)</p></td><td  ><p>2,078</p></td><td  ><p>3,990</p></td><td  ><p>2,713</p></td></tr><tr><td class="firstcol " ><p>Rec. 2020 Gamut Coverage</p></td><td  ><p>87.2%</p></td><td  ><p>87.3%</p></td><td  ><p>78.02%</p></td></tr></tbody></table></div><p>As far as HDR highlight brightness goes, the Bravia 7 II is just under what we measured on the Bravia 9 a few years ago. This is plenty bright for most viewing environments, and if Sony’s picture processing delivers a similar HDR presentation as the flagship Bravia 9, I’ll be thrilled.</p><div><blockquote><p>If Sony’s picture processing delivers a similar HDR presentation as the flagship Bravia 9, I’ll be thrilled.</p></blockquote></div><p>HDR brightness is shaping up to be one of the primary distinctions between the Bravia 7 II and the all-new Bravia 9 II. If you’re in the market for a Sony-engineered set and you’re chasing the brightest possible picture, you’ll have to splash out on the Bravia 9 II.</p><p>However, when it comes to overall color volume, the Bravia 7 II is right in line with its souped-up, RGB LED-powered sibling. Given that color is the bread and butter of RGB LED technology, it’s heartening to learn that Sony’s entry-level RGB LED TV is cooking with a similar set of ingredients as the brand’s top-shelf model.</p><p>And, while I’ll reserve my full judgment for after I’ve had a chance to spend some time with these new TVs, when I look at these test results, I can’t help but see the Bravia 7 II as a slightly stripped-down version of the original Bravia 9. This set was (and still is) one of my favorite Mini-LED TVs ever made, and while the Bravia 7 II isn’t quite as bright, it gets pretty close — all while delivering punchier-looking color.</p><h2 id="sony-bravia-7-ii-vs-bravia-9-ii-pricing-and-availability">Sony Bravia 7 II vs Bravia 9 II: pricing and 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="KGZxKdWq8FXSaaUB6WyWWA" name="Sony-Bravia-9-II-85-inch" alt="Lifestyle image of the Sony Bravia 9 II (85-inch model) wall mounted in a living room environment." src="https://cdn.mos.cms.futurecdn.net/KGZxKdWq8FXSaaUB6WyWWA-1920-80.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: Sony)</span></figcaption></figure><p>So, let’s say that you decide to go with the Bravia 7 II rather than springing for the flagship-level Bravia 9 II. How much money, exactly, are you saving?</p><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Sony Bravia 9 II</strong></p></td><td  ><p><strong>Sony Bravia 7 II</strong></p></td></tr><tr><td class="firstcol " ><p><strong>50 inch</strong></p></td><td  ><p>N/A</p></td><td  ><p>$1,599.99 </p></td></tr><tr><td class="firstcol " ><p><strong>55 inch</strong></p></td><td  ><p>N/A</p></td><td  ><p>$2,099.99</p></td></tr><tr><td class="firstcol " ><p><strong>65 inch</strong></p></td><td  ><p> $3,599.99</p></td><td  ><p>$2,599.99</p></td></tr><tr><td class="firstcol " ><p><strong>75 inch</strong></p></td><td  ><p>$4,599.99</p></td><td  ><p>$3,099.99 </p></td></tr><tr><td class="firstcol " ><p><strong>85 inch</strong></p></td><td  ><p>$6,499.99</p></td><td  ><p>$3,999.99</p></td></tr><tr><td class="firstcol " ><p><strong>98 inch</strong></p></td><td  ><p>N/A</p></td><td  ><p>$8,999.99</p></td></tr><tr><td class="firstcol " ><p><strong>115 inch</strong></p></td><td  ><p>$30,999.99</p></td><td  ><p>N/A</p></td></tr></tbody></table></div><p>Of the two, the Bravia 7 II is the only one available in sizes below 65 inches. If you’re looking for a 50- or 55-inch TV, the Bravia 7 II is your sole option.</p><p>But if you’re choosing between the two at the 65-, 75- or 85-inch level, you stand to save between $1,000 and $2,500 by going with the Bravia 7 II. Make no mistake: The Bravia 7 II’s pricing is on par with the MSRP of mid-range OLED TVs (like the <a href="https://www.tomsguide.com/tvs/oled-tvs/lg-c6-oled-review">LG C6</a>), but you’re saving a significant amount of cash by choosing the Bravia 7 II over the Bravia 9 II.</p><p>Compared to the original Bravia 9 (now a few years old), the Bravia 7 II is a bit pricier at launch. Right now, you can get the<a href="https://www.bestbuy.com/product/sony-65-class-bravia-9-mini-led-qled-4k-uhd-smart-google-tv-2024/J7XSRH59ZQ/sku/6578568?utm_source=feed&extStoreId=596&ref=212&loc=18467156568&gclsrc=aw.ds&gad_source=1&gad_campaignid=18470468791&gbraid=0AAAAAD-ORIhiR0dnNxsHa44B7IUDnVUDD&gclid=Cj0KCQjwz9_QBhD_ARIsADnSCfDkqKPCHm8j7c2n6GDy72LT-F8m8_ifi7AKHlWyUhkzthnbUcQSKkEaAjCkEALw_wcB"> 65-inch Sony Bravia 9 for $2,099 at Best Buy</a>. This is much more affordable than it was when it made its debut in 2024, but it’s $500 more than the Bravia 7 II, and the same price as the 55-inch Bravia 7 II.</p><h2 id="sony-bravia-7-ii-outlook-2">Sony Bravia 7 II: outlook</h2><p>As mentioned, we’re still hard at work on our full reviews of both the Bravia 7 II and the Bravia 9 II, so we’re still evaluating other aspects of these TVs’ performance. Until the dust settles, I’m hesitant to make any formal declarations about which TV is a better fit for various viewers.</p><p>That said, while the Bravia 9 II is shaping up to be the crown jewel of Sony’s 2026 TV lineup, I must admit that I am mighty excited to spend some quality time with the Bravia 7 II, too. Given Sony’s track record for delivering finely tuned, detail-oriented TVs with top-notch picture processing, the Bravia 7 II might prove to be the all-around best option for people who are curious about RGB LED technology, but who’d rather not shell out for a first-generation flagship.</p><div><blockquote><p>The Bravia 7 II might prove to be the all-around best option for people who are curious about RGB LED technology, but who’d rather not shell out for a first-generation flagship.</p></blockquote></div><p>Of course, Sony will be up against a number of entry-level RGB LED TVs that offer a similar compromise, including the Samsung R85H and the Hisense UR8. How it fares against those sets remains to be seen.</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/tvs/i-saw-sonys-bravia-9-ii-up-close-and-it-completely-shattered-my-expectations-heres-why-its-the-king-of-rgb-tvs#section-sony-bravia-9-ii-bravia-7-ii-price-and-availability">I saw Sony's Bravia 9 II up close and it completely shattered my expectations — here’s why it’s the king of RGB TVs</a></li><li><a href="https://www.tomsguide.com/tvs/samsungs-new-micro-rgb-tv-is-finally-here-for-usd3-200-but-id-rather-buy-this-oled-instead">Samsung's new Micro RGB TV is finally here — but I'd rather buy this OLED instead</a></li><li><a href="https://www.tomsguide.com/tvs/3-reasons-why-you-shouldnt-buy-an-rgb-led-tv-in-2026-and-1-reason-you-should">3 reasons why you shouldn't buy an RGB LED TV in 2026</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Forget Sony’s new $649 Collexion headphones — if you care about audio, these are the premium cans you should buy instead ]]></title>
                                                                                                <dc:content><![CDATA[ <p>In case you missed it, the other week <a href="https://www.tomsguide.com/audio/headphones/sony-just-launched-the-new-1000x-the-collexion-headphones-heres-everything-you-need-to-know-about-this-premium-audio-upgrade">Sony dropped its $649 1000X THE COLLEXION headphones</a>. But don’t worry, I’ll spare you the shouty caps and just refer to them as “1000X The Collexion” or simply “1000X” from now on. </p><p>An upgrade to the still fairly recent <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review">Sony WH1000-XM6</a>, the 1000X have stolen the XM6’s accolade of being Sony’s flagship over-ears. And naturally, that demands a premium in price. The 1000X will set you back $649. So, some dollars indeed.</p><p>Sony sent us a few pairs to try out, and I’ve gotta say, they’re fairly impressive. As it happens, though, when the <a href="https://www.tomsguide.com/audio/headphones/i-tested-sonys-usd650-fashion-headphones-the-wh-1000x-the-collexion-are-more-than-just-pretty">1000X</a> landed on my desk, I had at hand my daily drivers — which I think are the Sony headphones’ closest competitors: the <a href="https://www.tomsguide.com/audio/over-ear-headphones/i-just-tested-these-impeccable-hi-res-headphones-and-they-blow-away-the-airpods-max">Bowers & Wilkins Px8 S2</a>.</p><p>Naturally, then, I did a little back-to-back test to see which came out on top. Here’s what happened.</p><h2 id="but-first-a-note-on-price">But first, a note on price</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="mqZZVYkynnjYjHTHMxaAj" name="EmptyName 1.JPG" alt="Bowers & Wilkins Px8 S2 in blue" src="https://cdn.mos.cms.futurecdn.net/mqZZVYkynnjYjHTHMxaAj-1920-80.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">Bowers & Wilkins Px8 S2 in blue. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Now, some important context before we start: there’s a $150 delta between these two sets of cans, with the <a href="https://www.amazon.com/Bowers-Wilkins-Headphones-Cancelling-Transparency/dp/B0FH5QDTBQ/" target="_blank" rel="nofollow">Px8 S2 costing $799 at Amazon</a>. The Px8 S2 don’t drop by much in sales events either, although you can grab a set used (Like New) from Amazon for $686. The <a href="https://www.amazon.com/Sony-COLLEXION-Cancelling-Headphones-Microphones/dp/B0GW9MBZJ9/" target="_blank" rel="nofollow">Sony 1000X The Collexion come in $150 cheaper at $649</a>.</p><p>My point here is to see whether — if you can afford to drop big bucks into headphones to begin with — it’s worth stretching that extra bit further to the pricier cans (spoiler alert… I think it is). For the target demographic, I'd say it's reasonable to assume an extra $150 shouldn't be a huge issue.</p><div class="product"><a data-dimension112="02ba1ed5-0ae6-48f6-8b93-0851ebd4b5e0" data-action="Deal Block" data-label="Px8 S2" data-dimension48="Px8 S2" data-dimension25="$799" href="https://www.amazon.com/Bowers-Wilkins-Headphones-Cancelling-Transparency/dp/B0FH5QDTBQ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="o8sH4w7bcbuUXtPwckc9yn" name="Bowers & Wilkins Px8 S2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/o8sH4w7bcbuUXtPwckc9yn-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>We awarded the Px8 S2 our Editor's Choice award and 4.5 stars in our review. These cans are pricey, but do virtually everything right — Hi-Fi sound, strong battery life, excellent ANC and luxurious build quality.<a class="view-deal button" href="https://www.amazon.com/Bowers-Wilkins-Headphones-Cancelling-Transparency/dp/B0FH5QDTBQ/" target="_blank" rel="nofollow" data-dimension112="02ba1ed5-0ae6-48f6-8b93-0851ebd4b5e0" data-action="Deal Block" data-label="Px8 S2" data-dimension48="Px8 S2" data-dimension25="$799">View Deal</a></p></div><div class="product"><a data-dimension112="f0de3245-79bb-46c2-b360-fa8274f7d58e" data-action="Deal Block" data-label="1000X THE COLLEXION" data-dimension48="1000X THE COLLEXION" data-dimension25="$649.99" href="https://www.amazon.com/Sony-COLLEXION-Cancelling-Headphones-Microphones/dp/B0GW9MBZJ9/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="MsaMjcX9k2kWR6V2YBTq9h" name="sony 1000x the collexion" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/MsaMjcX9k2kWR6V2YBTq9h-1920-80.png" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony 1000X THE COLLEXION are the brand's new flagship, delivering higher fidelity and cleaner sound than the WH-1000XM6, albeit at a higher price. They're cheaper than the Px8 S2, and still great cans if you can't stretch an extra $150.<a class="view-deal button" href="https://www.amazon.com/Sony-COLLEXION-Cancelling-Headphones-Microphones/dp/B0GW9MBZJ9/" target="_blank" rel="nofollow" data-dimension112="f0de3245-79bb-46c2-b360-fa8274f7d58e" data-action="Deal Block" data-label="1000X THE COLLEXION" data-dimension48="1000X THE COLLEXION" data-dimension25="$649.99">View Deal</a></p></div><h2 id="sound-quality-a-close-run-thing">Sound quality: a close-run thing</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="2aDfyW37Szj5dCTPo8QzPF" name="Sony-WH-1000-The-Collexion-7" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/2aDfyW37Szj5dCTPo8QzPF-1920-80.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="caption-text">Sony 1000X The Collexion in white. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Admittedly, both of these headphones sound fantastic. I went into my back-to-back sound tests thinking the Px8 S2 would wipe the floor with the Sonys, but it wasn’t a total wash out. The 1000X are marketed by Sony as offering a much more refined and spacious sound than the WH1000-XM6 — which are tuned more in line with what the mass market wants: bass.</p><p>That said, I still find the 1000X a little too warm in their tuning — for my tastes anyway. Now, I actually enjoyed this in songs where bass isn’t the overriding element to begin with — Metallica’s <em>Seek & Destroy</em>, for example, where the added power on Lars’ kick drum complements the drive and energy of the song. </p><p><em>Listen along to my testing playlist on </em><a href="https://www.tomsguide.com/audio/qobuz-review"><em>Qobuz</em></a><em> using the widget below.</em></p><iframe allow="" height="390" width="378" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://widget.qobuz.com/playlist/59666746?zone=GB-en"></iframe><p>Elsewhere, I still found softer percussion, shimmering effects and vocals often felt a little buried by the 1000X bass, especially in low-heavy electronica like Burn Water’s super-subby, atmospheric <em>Ikigai</em>. At least via the Standard EQ preset. This wasn’t a huge problem — Sony’s app has a wide range of preset EQs and a custom EQ. I really liked the Clear EQ setting, which controlled the bass a little better, for my tastes at least. But I had to play around a fair bit with EQ on a regular basis (a lot of people like doing this, but I find it distracting).</p><p>By contrast, I haven’t really ever needed to play around with the Bowers & Wilkins EQ. These cans just sound so clean and neutral out of the box, with so much fidelity everywhere you listen. Bass is better controlled, with plenty of detail in the treble, giving really bassy songs like <em>Ikigai</em> much more clarity by allowing the soft, ethereal backing vocals and effects the space they need. There’s bags of mid-range definition, too, which added so much crunch to the distorted riffs on <em>Seek & Destroy</em>. </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="XF7aGGdDAhyxK5M7ZEs3Pf" name="Bowers & Wilkins PX8 S2" alt="Bowers & Wilkins PX8 S2" src="https://cdn.mos.cms.futurecdn.net/XF7aGGdDAhyxK5M7ZEs3Pf-1920-80.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="caption-text">Bowers & Wilkins Px8 S2 in brown. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Of note is that the 1000X use 30mm dynamic drivers. Size isn’t everything, of course, but I noticed perceptible distortion on some of the louder sub booms in <em>Ikigai</em>. The Px8 S2 suffered no distortion in the same areas, likely thanks to the larger 40mm drivers.</p><div><blockquote><p>"The 1000X have a 360 upmix mode. I was excited to try this out, but unfortunately found it borderline unlistenable."</p></blockquote></div><p>Both headphones are rather spacious for closed backs, but the Px8 S2 definitely have the edge, sounding noticeably cleaner, brighter and more open than the more intimate-feeling 1000X. Chris Stapleton’s <em>Death Row</em> was pleasingly dynamic on the Bowers & Wilkins, the bluesy guitar licks and Chris’ reverby vocals getting a little closer to the acoustic-esque experience of listening to the track through my <a href="https://www.audeze.com/products/lcd-5s" target="_blank" rel="nofollow">Audeze LCD5-S</a> open backs (although, obviously, the LCD5-S are in a different league, space-wise).</p><p>To supposedly help with that, the 1000X have a 360 upmix mode. I was excited to try this out, but unfortunately found it borderline unlistenable. I mean, it sorta works: Mk.Gee’s über spatial <em>Dream Police</em> was a perfect candidate, and the extra reverb and echoing made for a live-recording-esque kinda effect. But the sound was incredibly compressed, with a bucket load of audible noise.</p><p>An especially noticeable difference between the two is the Px8 S2’s greater detail at low volumes. We discussed this in our Px8 S2 review, so it was on my radar, but I hadn’t grasped the full extent until listening to them in earnest. Even at around 30% volume, the B&Ws retain an incredibly impressive amount of low-end texture and high/mid clarity. By contrast, I had to keep the 1000X at around 70% for a similar sound. Better for your ears, better for battery life, and better for the long term health of the drivers I guess. </p><h2 id="flawless-anc-on-both">Flawless ANC on both</h2><p>Both the 1000X and Px8 S2 have fantastic ANC. In fact, it’s very hard to draw a verdict here. They both do a better job than my $1,200 <a href="https://www.tomsguide.com/audio/over-ear-headphones/bang-and-olufsen-beoplay-h95-review-headphones">Bang & Olufsen Beoplay H95</a>. Using the 1000X in the office, they cut virtually everything, including road noise from the open window next to my desk. </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="NxJHavAUSUAirJC7FJVNj" name="EmptyName 2.JPG" alt="Bowers & Wilkins Px8 S2 in blue" src="https://cdn.mos.cms.futurecdn.net/NxJHavAUSUAirJC7FJVNj-1920-80.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>All I can say is that the Px8 S2 ANC is probably the best I’ve tested. Perhaps the strongest test was on the flight home from my honeymoon a couple of weeks ago. The seat in front hosted a screaming toddler, flanked by two dismayed parents, open-mouthed between kicks to the face. On the opposing aisle sat the similarly-unparented and only-slightly-older brother, listening to some mindlessly cheerful kids TV at full blast through his iPad speakers. In the midst of the chaos, a flight attendant fought desperately through the ruckus to ensure seatbelts were on — somehow, if the parents’ entitled faces were anything to go by, this was all <em>her</em> fault.</p><p>Tiny arms flailed. Dried fruit snacks flew. Someone probably lost an incisor. A flight attendant questioned her life choices by flying Ryanair. Many passengers did the same. Nightmare fuel, usually. But not with the Px8 S2.</p><p>Back in the sixth circle of hell — or the row behind the chaos — my wife looked aghast at me laughing maniacally. ‘<em>How can you laugh through this?’</em>  was the subtext. It was the poetry of watching all that happen in mime only, soundtracked instead by the screaming cacophony of Songs For The Deaf’s opener. <em><strong>“You can’t even hear it!”</strong></em></p><p>No matter how loud the hellspawn in front decided to be, nor how long through the flight it decided to be so, I was alright. The Px8 S2’s ANC would drown out everything. And it did. </p><iframe allow="" height="390" width="378" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://widget.qobuz.com/album/0060694934352?zone=GB-en"></iframe><h2 id="everyday-comforts">Everyday comforts</h2><p>A lot has been said online about the supreme comfort of the 1000X, and I agree… for the most part. They feel like a premium set of cans, and if I’d bought them, I wouldn’t feel shortchanged in the build quality department. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/KQuwNDpMpKqiXtbbBkB3Qf-1920-80.jpg" alt="Bowers & Wilkins PX8 S2" /><figcaption>Neither pair of headphones fold, and both come with a rigid carry case.<small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/pdw8ak4ZDABxE6JqwA294Y-1920-80.jpg" alt="Sony WH-1000X The Collexion" /><figcaption>Neither pair of headphones fold, and both come with a rigid carry case.<small role="credit">Future</small></figcaption></figure></figure><p>Again, though, the Bowers & Wilkins have the edge. They go beyond premium to feel luxurious. After around an hour of wearing the Sonys, my ears started to feel a little toasty. The Px8 S2 use a more breathable material, as I wore them for most of a seven-hour train journey through Norway with no issues whatsoever.</p><p>Both sets of cans are a pain in the butt to carry around, though, as they don’t fold and use bulky carry cases instead. The Sony’s case is markedly thinner at least, so that’s the set I’d pick if portability were my biggest concern. Which it isn’t. </p><p>Battery life is another win for the Px8 S2, which offer up to 30 hours. After my seven-hour Norwegian train journey, they’d dropped to 83%, which tallies to our full battery test when we reviewed them. Battery life on the 1000X is just 24 hours. Admittedly, this is only an issue if you’re travelling away from power for any elongated period of time — not really a thing nowadays with the ubiquity of portable power banks. </p><h2 id="frustrating-connectivity">Frustrating connectivity</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="5qQPjACo9Hn4vHeQ7hedik" name="Sony-WH-1000-The-Collexion-6" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/5qQPjACo9Hn4vHeQ7hedik-1920-80.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 touched on apps a little earlier when talking about EQ, but there’s more to be said on this. Sony’s app is great, don’t get me wrong, but it’s fundamental to the operation of the headphones, and that really grates on me. You seemingly can’t even connect to the 1000X without the companion app, which feels extremely restrictive — especially for a product you’ve paid $649 for. </p><p>When you do try to connect, the Sony companion app acts like a typical Sony companion app: janky as all hell. It took me 16 minutes to get the headphones to connect. 16 minutes. To hook up over Bluetooth.</p><p>I love that with the Px8 S2, the app is essentially optional. You don’t need to download it in order to get the headphones working, and the driver tuning and default EQ are so good out the box that I rarely, if ever, need to open the app for tweaks.</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="j2QtVm4uU83H4UDg37M3Qf" name="Bowers & Wilkins PX8 S2" alt="Bowers & Wilkins PX8 S2" src="https://cdn.mos.cms.futurecdn.net/j2QtVm4uU83H4UDg37M3Qf-1920-80.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="caption-text">The Bowers & Wilkins Px8 S2 come with a USB-C cable which can be used for wired playback. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Px8 S2 also have USB-C and 3.5mm connectivity, as opposed to only 3.5mm on the Sonys. That means, if you want to play wired from your phone with the 1000X, you’re stuck using the <em>technically</em> subpar phone DAC. Will it make a noticeable difference? Probably not. But I like knowing that I’m bypassing my smartphone DAC altogether and using the Px8 S2’s internal conversion over USB-C, getting the best I can out of the cans when using my phone.</p><p>Still, if you’re concerned, you could always invest in a portable DAC/amp, like the <a href="https://www.amazon.com/FiiO-JadeAudio-Portable-Adapter-Compatible/dp/B0CJ6RKV6X">FiiO KA13</a> ($65). Or my go-to portable DAC, the <a href="https://www.amazon.com/iFi-hip-dac3-Headphone-Enhancement-PowerMatch/dp/B0CKFKNNJH">iFi hipdac 3</a> ($199).</p><h2 id="the-final-word">The final word</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="giWHcvgNtcTZWxVq3ms83E" name="Sony 1000X vs BW Px8 S2" alt="A split image showing the Sony 1000X The Collexion on the left and the Bowers & Wilkins Px8 S2 on the right" src="https://cdn.mos.cms.futurecdn.net/giWHcvgNtcTZWxVq3ms83E-1920-80.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 class="product"><a data-dimension112="62976dd5-3528-419f-a1a6-75b25ffec43c" data-action="Deal Block" data-label="Px8 S2" data-dimension48="Px8 S2" data-dimension25="$799" href="https://www.amazon.com/Bowers-Wilkins-Headphones-Cancelling-Transparency/dp/B0FH5QDTBQ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="o8sH4w7bcbuUXtPwckc9yn" name="Bowers & Wilkins Px8 S2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/o8sH4w7bcbuUXtPwckc9yn-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>We awarded the Px8 S2 our Editor's Choice award and 4.5 stars in our review. These cans are pricey, but do virtually everything right — Hi-Fi sound, strong battery life, excellent ANC and luxurious build quality.<a class="view-deal button" href="https://www.amazon.com/Bowers-Wilkins-Headphones-Cancelling-Transparency/dp/B0FH5QDTBQ/" target="_blank" rel="nofollow" data-dimension112="62976dd5-3528-419f-a1a6-75b25ffec43c" data-action="Deal Block" data-label="Px8 S2" data-dimension48="Px8 S2" data-dimension25="$799">View Deal</a></p></div><p>So which would I buy out of the two? If you hadn’t already guessed, it’s the Bowers & Wilkins Px8 S2. Look, the Sony 1000X <u><em><strong>THE COLLEXION</strong></em></u><em> </em>are decent headphones. But when you start pricing cans in the ultra-premium range, you’re gonna have to start mixing it with the big boys. And the 1000X can’t quite mix it with the chief.</p><p>If you don’t have the extra $150, I doubt you’ll be disappointed by the 1000X. They look great, sound great, and feel great on your head. It’s just that the Px8 S2 do, well, just about everything better.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OarxKX"></div>                            </div>                            <script src="https://kwizly.com/embed/OarxKX.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/audio/headphones/apple-airpods-max-2-vs-sony-1000x-the-collexion">Apple AirPods Max 2 vs Sony 1000X The Collexion</a></li><li><a href="https://www.tomsguide.com/audio/earbuds/soundcore-liberty-5-pro-max-review">I tested Soundcore’s newest premium flagship earbuds — and they’re coming for the AirPods Pro 3’s crown</a></li><li><a href="https://www.tomsguide.com/audio/earbuds/im-ditching-my-over-ear-headphones-for-these-sony-in-ear-buds-to-beat-the-heat-this-summer-and-they-offer-bose-like-sound-for-a-lot-less">I’m ditching my over-ear headphones for these Sony earbuds to beat the heat this summer — and they offer Bose-like sound for a lot less</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/over-ear-headphones/forget-sonys-new-usd649-headphones-if-you-care-about-audio-these-are-the-premium-cans-you-should-buy-instead</link>
                                                                            <description>
                            <![CDATA[ I tested the Sony 1000X The Collexion versus the Bowers & Wilkins Px8 S2. Here's what happened. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">QMxbrT2hCr8hEu8q6zYUDK</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/giWHcvgNtcTZWxVq3ms83E-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 29 May 2026 05:45:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Over-Ear Headphones]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></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-320-70.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/giWHcvgNtcTZWxVq3ms83E-1920-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A split image showing the Sony 1000X The Collexion on the left and the Bowers &amp; Wilkins Px8 S2 on the right]]></media:description>                                                            <media:text><![CDATA[A split image showing the Sony 1000X The Collexion on the left and the Bowers &amp; Wilkins Px8 S2 on the right]]></media:text>
                                <media:title type="plain"><![CDATA[A split image showing the Sony 1000X The Collexion on the left and the Bowers &amp; Wilkins Px8 S2 on the right]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/giWHcvgNtcTZWxVq3ms83E-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>In case you missed it, the other week <a href="https://www.tomsguide.com/audio/headphones/sony-just-launched-the-new-1000x-the-collexion-headphones-heres-everything-you-need-to-know-about-this-premium-audio-upgrade">Sony dropped its $649 1000X THE COLLEXION headphones</a>. But don’t worry, I’ll spare you the shouty caps and just refer to them as “1000X The Collexion” or simply “1000X” from now on. </p><p>An upgrade to the still fairly recent <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review">Sony WH1000-XM6</a>, the 1000X have stolen the XM6’s accolade of being Sony’s flagship over-ears. And naturally, that demands a premium in price. The 1000X will set you back $649. So, some dollars indeed.</p><p>Sony sent us a few pairs to try out, and I’ve gotta say, they’re fairly impressive. As it happens, though, when the <a href="https://www.tomsguide.com/audio/headphones/i-tested-sonys-usd650-fashion-headphones-the-wh-1000x-the-collexion-are-more-than-just-pretty">1000X</a> landed on my desk, I had at hand my daily drivers — which I think are the Sony headphones’ closest competitors: the <a href="https://www.tomsguide.com/audio/over-ear-headphones/i-just-tested-these-impeccable-hi-res-headphones-and-they-blow-away-the-airpods-max">Bowers & Wilkins Px8 S2</a>.</p><p>Naturally, then, I did a little back-to-back test to see which came out on top. Here’s what happened.</p><h2 id="but-first-a-note-on-price">But first, a note on price</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="mqZZVYkynnjYjHTHMxaAj" name="EmptyName 1.JPG" alt="Bowers & Wilkins Px8 S2 in blue" src="https://cdn.mos.cms.futurecdn.net/mqZZVYkynnjYjHTHMxaAj-1920-80.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">Bowers & Wilkins Px8 S2 in blue. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Now, some important context before we start: there’s a $150 delta between these two sets of cans, with the <a href="https://www.amazon.com/Bowers-Wilkins-Headphones-Cancelling-Transparency/dp/B0FH5QDTBQ/" target="_blank" rel="nofollow">Px8 S2 costing $799 at Amazon</a>. The Px8 S2 don’t drop by much in sales events either, although you can grab a set used (Like New) from Amazon for $686. The <a href="https://www.amazon.com/Sony-COLLEXION-Cancelling-Headphones-Microphones/dp/B0GW9MBZJ9/" target="_blank" rel="nofollow">Sony 1000X The Collexion come in $150 cheaper at $649</a>.</p><p>My point here is to see whether — if you can afford to drop big bucks into headphones to begin with — it’s worth stretching that extra bit further to the pricier cans (spoiler alert… I think it is). For the target demographic, I'd say it's reasonable to assume an extra $150 shouldn't be a huge issue.</p><div class="product"><a data-dimension112="02ba1ed5-0ae6-48f6-8b93-0851ebd4b5e0" data-action="Deal Block" data-label="Px8 S2" data-dimension48="Px8 S2" data-dimension25="$799" href="https://www.amazon.com/Bowers-Wilkins-Headphones-Cancelling-Transparency/dp/B0FH5QDTBQ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="o8sH4w7bcbuUXtPwckc9yn" name="Bowers & Wilkins Px8 S2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/o8sH4w7bcbuUXtPwckc9yn-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>We awarded the Px8 S2 our Editor's Choice award and 4.5 stars in our review. These cans are pricey, but do virtually everything right — Hi-Fi sound, strong battery life, excellent ANC and luxurious build quality.<a class="view-deal button" href="https://www.amazon.com/Bowers-Wilkins-Headphones-Cancelling-Transparency/dp/B0FH5QDTBQ/" target="_blank" rel="nofollow" data-dimension112="02ba1ed5-0ae6-48f6-8b93-0851ebd4b5e0" data-action="Deal Block" data-label="Px8 S2" data-dimension48="Px8 S2" data-dimension25="$799">View Deal</a></p></div><div class="product"><a data-dimension112="f0de3245-79bb-46c2-b360-fa8274f7d58e" data-action="Deal Block" data-label="1000X THE COLLEXION" data-dimension48="1000X THE COLLEXION" data-dimension25="$649.99" href="https://www.amazon.com/Sony-COLLEXION-Cancelling-Headphones-Microphones/dp/B0GW9MBZJ9/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="MsaMjcX9k2kWR6V2YBTq9h" name="sony 1000x the collexion" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/MsaMjcX9k2kWR6V2YBTq9h-1920-80.png" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony 1000X THE COLLEXION are the brand's new flagship, delivering higher fidelity and cleaner sound than the WH-1000XM6, albeit at a higher price. They're cheaper than the Px8 S2, and still great cans if you can't stretch an extra $150.<a class="view-deal button" href="https://www.amazon.com/Sony-COLLEXION-Cancelling-Headphones-Microphones/dp/B0GW9MBZJ9/" target="_blank" rel="nofollow" data-dimension112="f0de3245-79bb-46c2-b360-fa8274f7d58e" data-action="Deal Block" data-label="1000X THE COLLEXION" data-dimension48="1000X THE COLLEXION" data-dimension25="$649.99">View Deal</a></p></div><h2 id="sound-quality-a-close-run-thing">Sound quality: a close-run thing</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="2aDfyW37Szj5dCTPo8QzPF" name="Sony-WH-1000-The-Collexion-7" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/2aDfyW37Szj5dCTPo8QzPF-1920-80.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="caption-text">Sony 1000X The Collexion in white. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Admittedly, both of these headphones sound fantastic. I went into my back-to-back sound tests thinking the Px8 S2 would wipe the floor with the Sonys, but it wasn’t a total wash out. The 1000X are marketed by Sony as offering a much more refined and spacious sound than the WH1000-XM6 — which are tuned more in line with what the mass market wants: bass.</p><p>That said, I still find the 1000X a little too warm in their tuning — for my tastes anyway. Now, I actually enjoyed this in songs where bass isn’t the overriding element to begin with — Metallica’s <em>Seek & Destroy</em>, for example, where the added power on Lars’ kick drum complements the drive and energy of the song. </p><p><em>Listen along to my testing playlist on </em><a href="https://www.tomsguide.com/audio/qobuz-review"><em>Qobuz</em></a><em> using the widget below.</em></p><iframe allow="" height="390" width="378" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://widget.qobuz.com/playlist/59666746?zone=GB-en"></iframe><p>Elsewhere, I still found softer percussion, shimmering effects and vocals often felt a little buried by the 1000X bass, especially in low-heavy electronica like Burn Water’s super-subby, atmospheric <em>Ikigai</em>. At least via the Standard EQ preset. This wasn’t a huge problem — Sony’s app has a wide range of preset EQs and a custom EQ. I really liked the Clear EQ setting, which controlled the bass a little better, for my tastes at least. But I had to play around a fair bit with EQ on a regular basis (a lot of people like doing this, but I find it distracting).</p><p>By contrast, I haven’t really ever needed to play around with the Bowers & Wilkins EQ. These cans just sound so clean and neutral out of the box, with so much fidelity everywhere you listen. Bass is better controlled, with plenty of detail in the treble, giving really bassy songs like <em>Ikigai</em> much more clarity by allowing the soft, ethereal backing vocals and effects the space they need. There’s bags of mid-range definition, too, which added so much crunch to the distorted riffs on <em>Seek & Destroy</em>. </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="XF7aGGdDAhyxK5M7ZEs3Pf" name="Bowers & Wilkins PX8 S2" alt="Bowers & Wilkins PX8 S2" src="https://cdn.mos.cms.futurecdn.net/XF7aGGdDAhyxK5M7ZEs3Pf-1920-80.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="caption-text">Bowers & Wilkins Px8 S2 in brown. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Of note is that the 1000X use 30mm dynamic drivers. Size isn’t everything, of course, but I noticed perceptible distortion on some of the louder sub booms in <em>Ikigai</em>. The Px8 S2 suffered no distortion in the same areas, likely thanks to the larger 40mm drivers.</p><div><blockquote><p>"The 1000X have a 360 upmix mode. I was excited to try this out, but unfortunately found it borderline unlistenable."</p></blockquote></div><p>Both headphones are rather spacious for closed backs, but the Px8 S2 definitely have the edge, sounding noticeably cleaner, brighter and more open than the more intimate-feeling 1000X. Chris Stapleton’s <em>Death Row</em> was pleasingly dynamic on the Bowers & Wilkins, the bluesy guitar licks and Chris’ reverby vocals getting a little closer to the acoustic-esque experience of listening to the track through my <a href="https://www.audeze.com/products/lcd-5s" target="_blank" rel="nofollow">Audeze LCD5-S</a> open backs (although, obviously, the LCD5-S are in a different league, space-wise).</p><p>To supposedly help with that, the 1000X have a 360 upmix mode. I was excited to try this out, but unfortunately found it borderline unlistenable. I mean, it sorta works: Mk.Gee’s über spatial <em>Dream Police</em> was a perfect candidate, and the extra reverb and echoing made for a live-recording-esque kinda effect. But the sound was incredibly compressed, with a bucket load of audible noise.</p><p>An especially noticeable difference between the two is the Px8 S2’s greater detail at low volumes. We discussed this in our Px8 S2 review, so it was on my radar, but I hadn’t grasped the full extent until listening to them in earnest. Even at around 30% volume, the B&Ws retain an incredibly impressive amount of low-end texture and high/mid clarity. By contrast, I had to keep the 1000X at around 70% for a similar sound. Better for your ears, better for battery life, and better for the long term health of the drivers I guess. </p><h2 id="flawless-anc-on-both">Flawless ANC on both</h2><p>Both the 1000X and Px8 S2 have fantastic ANC. In fact, it’s very hard to draw a verdict here. They both do a better job than my $1,200 <a href="https://www.tomsguide.com/audio/over-ear-headphones/bang-and-olufsen-beoplay-h95-review-headphones">Bang & Olufsen Beoplay H95</a>. Using the 1000X in the office, they cut virtually everything, including road noise from the open window next to my desk. </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="NxJHavAUSUAirJC7FJVNj" name="EmptyName 2.JPG" alt="Bowers & Wilkins Px8 S2 in blue" src="https://cdn.mos.cms.futurecdn.net/NxJHavAUSUAirJC7FJVNj-1920-80.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>All I can say is that the Px8 S2 ANC is probably the best I’ve tested. Perhaps the strongest test was on the flight home from my honeymoon a couple of weeks ago. The seat in front hosted a screaming toddler, flanked by two dismayed parents, open-mouthed between kicks to the face. On the opposing aisle sat the similarly-unparented and only-slightly-older brother, listening to some mindlessly cheerful kids TV at full blast through his iPad speakers. In the midst of the chaos, a flight attendant fought desperately through the ruckus to ensure seatbelts were on — somehow, if the parents’ entitled faces were anything to go by, this was all <em>her</em> fault.</p><p>Tiny arms flailed. Dried fruit snacks flew. Someone probably lost an incisor. A flight attendant questioned her life choices by flying Ryanair. Many passengers did the same. Nightmare fuel, usually. But not with the Px8 S2.</p><p>Back in the sixth circle of hell — or the row behind the chaos — my wife looked aghast at me laughing maniacally. ‘<em>How can you laugh through this?’</em>  was the subtext. It was the poetry of watching all that happen in mime only, soundtracked instead by the screaming cacophony of Songs For The Deaf’s opener. <em><strong>“You can’t even hear it!”</strong></em></p><p>No matter how loud the hellspawn in front decided to be, nor how long through the flight it decided to be so, I was alright. The Px8 S2’s ANC would drown out everything. And it did. </p><iframe allow="" height="390" width="378" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://widget.qobuz.com/album/0060694934352?zone=GB-en"></iframe><h2 id="everyday-comforts">Everyday comforts</h2><p>A lot has been said online about the supreme comfort of the 1000X, and I agree… for the most part. They feel like a premium set of cans, and if I’d bought them, I wouldn’t feel shortchanged in the build quality department. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/KQuwNDpMpKqiXtbbBkB3Qf-1920-80.jpg" alt="Bowers & Wilkins PX8 S2" /><figcaption>Neither pair of headphones fold, and both come with a rigid carry case.<small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/pdw8ak4ZDABxE6JqwA294Y-1920-80.jpg" alt="Sony WH-1000X The Collexion" /><figcaption>Neither pair of headphones fold, and both come with a rigid carry case.<small role="credit">Future</small></figcaption></figure></figure><p>Again, though, the Bowers & Wilkins have the edge. They go beyond premium to feel luxurious. After around an hour of wearing the Sonys, my ears started to feel a little toasty. The Px8 S2 use a more breathable material, as I wore them for most of a seven-hour train journey through Norway with no issues whatsoever.</p><p>Both sets of cans are a pain in the butt to carry around, though, as they don’t fold and use bulky carry cases instead. The Sony’s case is markedly thinner at least, so that’s the set I’d pick if portability were my biggest concern. Which it isn’t. </p><p>Battery life is another win for the Px8 S2, which offer up to 30 hours. After my seven-hour Norwegian train journey, they’d dropped to 83%, which tallies to our full battery test when we reviewed them. Battery life on the 1000X is just 24 hours. Admittedly, this is only an issue if you’re travelling away from power for any elongated period of time — not really a thing nowadays with the ubiquity of portable power banks. </p><h2 id="frustrating-connectivity">Frustrating connectivity</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="5qQPjACo9Hn4vHeQ7hedik" name="Sony-WH-1000-The-Collexion-6" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/5qQPjACo9Hn4vHeQ7hedik-1920-80.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 touched on apps a little earlier when talking about EQ, but there’s more to be said on this. Sony’s app is great, don’t get me wrong, but it’s fundamental to the operation of the headphones, and that really grates on me. You seemingly can’t even connect to the 1000X without the companion app, which feels extremely restrictive — especially for a product you’ve paid $649 for. </p><p>When you do try to connect, the Sony companion app acts like a typical Sony companion app: janky as all hell. It took me 16 minutes to get the headphones to connect. 16 minutes. To hook up over Bluetooth.</p><p>I love that with the Px8 S2, the app is essentially optional. You don’t need to download it in order to get the headphones working, and the driver tuning and default EQ are so good out the box that I rarely, if ever, need to open the app for tweaks.</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="j2QtVm4uU83H4UDg37M3Qf" name="Bowers & Wilkins PX8 S2" alt="Bowers & Wilkins PX8 S2" src="https://cdn.mos.cms.futurecdn.net/j2QtVm4uU83H4UDg37M3Qf-1920-80.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="caption-text">The Bowers & Wilkins Px8 S2 come with a USB-C cable which can be used for wired playback. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Px8 S2 also have USB-C and 3.5mm connectivity, as opposed to only 3.5mm on the Sonys. That means, if you want to play wired from your phone with the 1000X, you’re stuck using the <em>technically</em> subpar phone DAC. Will it make a noticeable difference? Probably not. But I like knowing that I’m bypassing my smartphone DAC altogether and using the Px8 S2’s internal conversion over USB-C, getting the best I can out of the cans when using my phone.</p><p>Still, if you’re concerned, you could always invest in a portable DAC/amp, like the <a href="https://www.amazon.com/FiiO-JadeAudio-Portable-Adapter-Compatible/dp/B0CJ6RKV6X">FiiO KA13</a> ($65). Or my go-to portable DAC, the <a href="https://www.amazon.com/iFi-hip-dac3-Headphone-Enhancement-PowerMatch/dp/B0CKFKNNJH">iFi hipdac 3</a> ($199).</p><h2 id="the-final-word">The final word</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="giWHcvgNtcTZWxVq3ms83E" name="Sony 1000X vs BW Px8 S2" alt="A split image showing the Sony 1000X The Collexion on the left and the Bowers & Wilkins Px8 S2 on the right" src="https://cdn.mos.cms.futurecdn.net/giWHcvgNtcTZWxVq3ms83E-1920-80.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 class="product"><a data-dimension112="62976dd5-3528-419f-a1a6-75b25ffec43c" data-action="Deal Block" data-label="Px8 S2" data-dimension48="Px8 S2" data-dimension25="$799" href="https://www.amazon.com/Bowers-Wilkins-Headphones-Cancelling-Transparency/dp/B0FH5QDTBQ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1000px;"><p class="vanilla-image-block" style="padding-top:120.00%;"><img id="o8sH4w7bcbuUXtPwckc9yn" name="Bowers & Wilkins Px8 S2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/o8sH4w7bcbuUXtPwckc9yn-1920-80.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>We awarded the Px8 S2 our Editor's Choice award and 4.5 stars in our review. These cans are pricey, but do virtually everything right — Hi-Fi sound, strong battery life, excellent ANC and luxurious build quality.<a class="view-deal button" href="https://www.amazon.com/Bowers-Wilkins-Headphones-Cancelling-Transparency/dp/B0FH5QDTBQ/" target="_blank" rel="nofollow" data-dimension112="62976dd5-3528-419f-a1a6-75b25ffec43c" data-action="Deal Block" data-label="Px8 S2" data-dimension48="Px8 S2" data-dimension25="$799">View Deal</a></p></div><p>So which would I buy out of the two? If you hadn’t already guessed, it’s the Bowers & Wilkins Px8 S2. Look, the Sony 1000X <u><em><strong>THE COLLEXION</strong></em></u><em> </em>are decent headphones. But when you start pricing cans in the ultra-premium range, you’re gonna have to start mixing it with the big boys. And the 1000X can’t quite mix it with the chief.</p><p>If you don’t have the extra $150, I doubt you’ll be disappointed by the 1000X. They look great, sound great, and feel great on your head. It’s just that the Px8 S2 do, well, just about everything better.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OarxKX"></div>                            </div>                            <script src="https://kwizly.com/embed/OarxKX.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/audio/headphones/apple-airpods-max-2-vs-sony-1000x-the-collexion">Apple AirPods Max 2 vs Sony 1000X The Collexion</a></li><li><a href="https://www.tomsguide.com/audio/earbuds/soundcore-liberty-5-pro-max-review">I tested Soundcore’s newest premium flagship earbuds — and they’re coming for the AirPods Pro 3’s crown</a></li><li><a href="https://www.tomsguide.com/audio/earbuds/im-ditching-my-over-ear-headphones-for-these-sony-in-ear-buds-to-beat-the-heat-this-summer-and-they-offer-bose-like-sound-for-a-lot-less">I’m ditching my over-ear headphones for these Sony earbuds to beat the heat this summer — and they offer Bose-like sound for a lot less</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I've been testing Sony's new wearable AC and it's changed the way I survive summer heat — the tech is actually genius ]]></title>
                                                                                                <dc:content><![CDATA[ <p>This is the secret gadget that nobody knows you're wearing, but keeps you cool or warm based on your environment. The<a href="https://www.tomsguide.com/home/reon-pocket-pro-plus-review"> Reon Pocket Pro Plus</a> has seriously changed the way I've been regulating my body temperature while at home, out and about, or at the office. </p><p>It offers cooling and warming settings, meaning that I've always been able to have my perfect temperature no matter the weather. I've always used handheld fans, but this year I'm going to rely on the Reon. </p><p>At the time of writing, the Reon Pocket Pro Plus isn't available in the U.S. market. That release is planned for Summer 2026, so it's not too long to wait, and you can <a href="https://electronics.sony.com/t/reonpocket" target="_blank">sign up for launch news </a>on Sony's website. It's currently available to purchase in the U.K. for<a href="https://www.amazon.co.uk/REON-POCKET-Temperature-Lightweight-Comfortable-Light-Grey/dp/B0GTQ1FGVD" target="_blank" rel="nofollow"> £199 on Amazon</a>. </p><div class="product"><a data-dimension112="95293b5a-da2f-4c46-8a14-718bf8746758" data-action="Deal Block" data-label="Pocket Pro Plus" data-dimension48="Pocket Pro Plus" data-dimension25="£199" href="https://www.amazon.co.uk/REON-POCKET-Temperature-Lightweight-Comfortable-Light-Grey/dp/B0GTQ1FGVD" 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="tYqnTFuCHv4s8PQyzaCYSJ" name="reon-pocket-pro-plus-wearable-cooling--h-afa58840-9002-46b9-b4f5-fd0e213144f5.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/tYqnTFuCHv4s8PQyzaCYSJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Reon Pocket Pro Plus is a unique, customizable personal temperature-regulation device that provides effective heating and cooling at the click of a button. At the time of writing, it's only available for purchase in the U.K., but it will be launching in the U.S. for Summer 2026. I appreciate the responsive, easy-to-navigate app and Smart Modes that eliminate constant manual adjustments. The battery can last a full working day, and your phone can be used as a remote for easy control.<a class="view-deal button" href="https://www.amazon.co.uk/REON-POCKET-Temperature-Lightweight-Comfortable-Light-Grey/dp/B0GTQ1FGVD" target="_blank" rel="nofollow" data-dimension112="95293b5a-da2f-4c46-8a14-718bf8746758" data-action="Deal Block" data-label="Pocket Pro Plus" data-dimension48="Pocket Pro Plus" data-dimension25="£199">View Deal</a></p></div>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7640542684305804558" data-video-id="7640542684305804558" 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="♬ 90s style hip hop(1595799) - staindgla22" href="https://www.tiktok.com/music/90s-style-hip-hop-1595799-7439424561718511633">♬ 90s style hip hop(1595799) - staindgla22</a></section>                    </blockquote></div>                <h2 id="how-the-reon-works">How the Reon works</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="USNPwupqfGtztRH4sB3oTA" name="Sony Reon Pocket Pro Plus" alt="Sony Reon Pocket Pro Plus photographed on a gray background" src="https://cdn.mos.cms.futurecdn.net/USNPwupqfGtztRH4sB3oTA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>It's not a fan, and it's not a heater. So what is it? </p><p>Well, the Reon is unlike any thermo device I've used before. It is worn on your back under your clothes and works by using a stainless steel plate that sits at the top of your back to heat and cool the skin. </p><p>When the blood vessels under the skin detect this temperature change, it tricks your internal thermostat into thinking you're cooler or warmer than you are. And I was surprised at how quickly this works. </p><p>There's a tiny fan at the top of the device that pulls air through; this is connected to a vent that sticks out of the top of your shirt, but it's hardly noticeable. And if you have long hair like me, you can't see it at all. It's also completely silent. </p><h2 id="surprisingly-comfortable">Surprisingly comfortable</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="QfNAoEiiUHSs5mEirLy3WA" name="Sony Reon Pocket Pro Plus" alt="Sony Reon Pocket Pro Plus and sensor photographed on a gray background" src="https://cdn.mos.cms.futurecdn.net/QfNAoEiiUHSs5mEirLy3WA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>When I first checked out the design of the Reon, I had to admit, I was a little skeptical. I mean, this thing has arms that wrap around my neck to keep it in place, and I wasn't sure if that was going to be entirely comfortable for the day. But oh, how wrong I was. </p><p>Once I found the correct positioning for the arms, I felt totally secure. But that said, this isn't something you'd be able to wear while working out; it's just ideal for temperature control while walking or lounging around.</p><h2 id="how-it-compares-for-the-summer">How it compares for the summer</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="EWMR4DjTQNA4sQKZLDCkGA" name="Sony Reon Pocket Pro Plus" alt="swappable vent of the Sony Reon Pocket Pro Plus" src="https://cdn.mos.cms.futurecdn.net/EWMR4DjTQNA4sQKZLDCkGA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>So, as we head into the summer, how does the Reon hold up when compared to other cooling devices?</p><p>First of all, the Reon is discreet. It's not like a handheld fan where you need to be wafting it around your face to feel the effects. It's going to take on the heat like a silent assassin. </p><p>You also have a lot more control than when you use a handheld fan. Take the <a href="https://www.tomsguide.com/home/dyson-just-launched-its-first-ever-portable-fan-and-it-could-be-the-gadget-of-the-summer">Dyson HushJet Mini Cool,</a> for example. It's the hottest — or should I say <em>coolest — </em>fan on the market right now, but you can't make actual temperature adjustments. It just blows air at you for sweat to evaporate from your skin quicker. You can actually select the temperature you want with the Reon Pocket Pro Plus. </p><p>I'm excited to get using the Reon through the summer months and to find out more about the thermo device, including more information on the U.S. launch, you can read my full <a href="https://www.tomsguide.com/home/reon-pocket-pro-plus-review">Reon Pocket Pro Plus review</a>. </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/reon-pocket-pro-plus-review"><strong>Forget fans, I've been testing Sony's new wearable thermo device — and it's the gadget of the future</strong></a></li><li><a href="https://www.tomsguide.com/wellness/fitness/columbia-drainmaker-review"><strong>I’ve been testing these hybrid shoes, and they’re the beach day addition I never realized I needed</strong></a></li><li><a href="https://www.tomsguide.com/wellness/workouts/stop-doing-100s-of-sit-ups-im-a-personal-trainer-and-i-use-bear-plank-openers-to-build-strong-abs-and-hips-instead"><strong>Stop doing 100s of sit-ups: I'm a personal trainer, and I use 'bear plank openers' to build strong abs and hips instead</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/ive-been-testing-sonys-new-wearable-ac-and-its-changed-the-way-i-survive-summer-heat-the-tech-is-actually-genius</link>
                                                                            <description>
                            <![CDATA[ The Reon Pocket Pro Plus is a discrete device that you wear under your clothing to regulate body temperature, and it's become my staple companion for summer. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">PyZpvJBKJWBNtNvmCykkWH</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/6FU6iwrv7XELRozAbxYDWA-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 24 May 2026 09:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Home]]></category>
                                                                                                <author><![CDATA[ ashley.thieme@futurenet.com (Ashley Thieme) ]]></author>                    <dc:creator><![CDATA[ Ashley Thieme ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3AWovHjApwuNrSGRS6WBcL-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ashley Thieme is a staff writer on the Reviews team at Tom’s Guide where she tests out the latest tech so you can know what’s going to be worth your time and money. She has a master’s degree in Magazine Journalism and a bachelor’s degree in Journalism, Media and Sociology from Cardiff University. She has bylines in titles including Women’s Health UK, sharing the latest health and wellness news stories, and Virgin Radio UK, providing the latest entertainment news and working on celebrity interviews. She has experience reporting on a variety of topics including music, literature, motorsport, entertainment and health. In previously published work, she has reviewed live music events, books, and wellness products. She values the importance of tech enhancing your life rather than taking over, and as a music fanatic, she is always looking for the best way to listen to new music releases. Discovering the top audio equipment that enhances sound quality and provides optimum comfort is what Ashley does best. In her spare time, Ashley enjoys hitting her reading goals on Goodreads by getting into the latest novels with a cup of tea as well as getting out in the Welsh mountains for a good hike on the weekend.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/6FU6iwrv7XELRozAbxYDWA-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sony Reon Pocket Pro Plus photographed in front of a blue background]]></media:description>                                                            <media:text><![CDATA[Sony Reon Pocket Pro Plus photographed in front of a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[Sony Reon Pocket Pro Plus photographed in front of a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/6FU6iwrv7XELRozAbxYDWA-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>This is the secret gadget that nobody knows you're wearing, but keeps you cool or warm based on your environment. The<a href="https://www.tomsguide.com/home/reon-pocket-pro-plus-review"> Reon Pocket Pro Plus</a> has seriously changed the way I've been regulating my body temperature while at home, out and about, or at the office. </p><p>It offers cooling and warming settings, meaning that I've always been able to have my perfect temperature no matter the weather. I've always used handheld fans, but this year I'm going to rely on the Reon. </p><p>At the time of writing, the Reon Pocket Pro Plus isn't available in the U.S. market. That release is planned for Summer 2026, so it's not too long to wait, and you can <a href="https://electronics.sony.com/t/reonpocket" target="_blank">sign up for launch news </a>on Sony's website. It's currently available to purchase in the U.K. for<a href="https://www.amazon.co.uk/REON-POCKET-Temperature-Lightweight-Comfortable-Light-Grey/dp/B0GTQ1FGVD" target="_blank" rel="nofollow"> £199 on Amazon</a>. </p><div class="product"><a data-dimension112="95293b5a-da2f-4c46-8a14-718bf8746758" data-action="Deal Block" data-label="Pocket Pro Plus" data-dimension48="Pocket Pro Plus" data-dimension25="£199" href="https://www.amazon.co.uk/REON-POCKET-Temperature-Lightweight-Comfortable-Light-Grey/dp/B0GTQ1FGVD" 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="tYqnTFuCHv4s8PQyzaCYSJ" name="reon-pocket-pro-plus-wearable-cooling--h-afa58840-9002-46b9-b4f5-fd0e213144f5.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/tYqnTFuCHv4s8PQyzaCYSJ-1920-80.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Reon Pocket Pro Plus is a unique, customizable personal temperature-regulation device that provides effective heating and cooling at the click of a button. At the time of writing, it's only available for purchase in the U.K., but it will be launching in the U.S. for Summer 2026. I appreciate the responsive, easy-to-navigate app and Smart Modes that eliminate constant manual adjustments. The battery can last a full working day, and your phone can be used as a remote for easy control.<a class="view-deal button" href="https://www.amazon.co.uk/REON-POCKET-Temperature-Lightweight-Comfortable-Light-Grey/dp/B0GTQ1FGVD" target="_blank" rel="nofollow" data-dimension112="95293b5a-da2f-4c46-8a14-718bf8746758" data-action="Deal Block" data-label="Pocket Pro Plus" data-dimension48="Pocket Pro Plus" data-dimension25="£199">View Deal</a></p></div>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7640542684305804558" data-video-id="7640542684305804558" 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="♬ 90s style hip hop(1595799) - staindgla22" href="https://www.tiktok.com/music/90s-style-hip-hop-1595799-7439424561718511633">♬ 90s style hip hop(1595799) - staindgla22</a></section>                    </blockquote></div>                <h2 id="how-the-reon-works">How the Reon works</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="USNPwupqfGtztRH4sB3oTA" name="Sony Reon Pocket Pro Plus" alt="Sony Reon Pocket Pro Plus photographed on a gray background" src="https://cdn.mos.cms.futurecdn.net/USNPwupqfGtztRH4sB3oTA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>It's not a fan, and it's not a heater. So what is it? </p><p>Well, the Reon is unlike any thermo device I've used before. It is worn on your back under your clothes and works by using a stainless steel plate that sits at the top of your back to heat and cool the skin. </p><p>When the blood vessels under the skin detect this temperature change, it tricks your internal thermostat into thinking you're cooler or warmer than you are. And I was surprised at how quickly this works. </p><p>There's a tiny fan at the top of the device that pulls air through; this is connected to a vent that sticks out of the top of your shirt, but it's hardly noticeable. And if you have long hair like me, you can't see it at all. It's also completely silent. </p><h2 id="surprisingly-comfortable">Surprisingly comfortable</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="QfNAoEiiUHSs5mEirLy3WA" name="Sony Reon Pocket Pro Plus" alt="Sony Reon Pocket Pro Plus and sensor photographed on a gray background" src="https://cdn.mos.cms.futurecdn.net/QfNAoEiiUHSs5mEirLy3WA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>When I first checked out the design of the Reon, I had to admit, I was a little skeptical. I mean, this thing has arms that wrap around my neck to keep it in place, and I wasn't sure if that was going to be entirely comfortable for the day. But oh, how wrong I was. </p><p>Once I found the correct positioning for the arms, I felt totally secure. But that said, this isn't something you'd be able to wear while working out; it's just ideal for temperature control while walking or lounging around.</p><h2 id="how-it-compares-for-the-summer">How it compares for the summer</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="EWMR4DjTQNA4sQKZLDCkGA" name="Sony Reon Pocket Pro Plus" alt="swappable vent of the Sony Reon Pocket Pro Plus" src="https://cdn.mos.cms.futurecdn.net/EWMR4DjTQNA4sQKZLDCkGA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>So, as we head into the summer, how does the Reon hold up when compared to other cooling devices?</p><p>First of all, the Reon is discreet. It's not like a handheld fan where you need to be wafting it around your face to feel the effects. It's going to take on the heat like a silent assassin. </p><p>You also have a lot more control than when you use a handheld fan. Take the <a href="https://www.tomsguide.com/home/dyson-just-launched-its-first-ever-portable-fan-and-it-could-be-the-gadget-of-the-summer">Dyson HushJet Mini Cool,</a> for example. It's the hottest — or should I say <em>coolest — </em>fan on the market right now, but you can't make actual temperature adjustments. It just blows air at you for sweat to evaporate from your skin quicker. You can actually select the temperature you want with the Reon Pocket Pro Plus. </p><p>I'm excited to get using the Reon through the summer months and to find out more about the thermo device, including more information on the U.S. launch, you can read my full <a href="https://www.tomsguide.com/home/reon-pocket-pro-plus-review">Reon Pocket Pro Plus review</a>. </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/reon-pocket-pro-plus-review"><strong>Forget fans, I've been testing Sony's new wearable thermo device — and it's the gadget of the future</strong></a></li><li><a href="https://www.tomsguide.com/wellness/fitness/columbia-drainmaker-review"><strong>I’ve been testing these hybrid shoes, and they’re the beach day addition I never realized I needed</strong></a></li><li><a href="https://www.tomsguide.com/wellness/workouts/stop-doing-100s-of-sit-ups-im-a-personal-trainer-and-i-use-bear-plank-openers-to-build-strong-abs-and-hips-instead"><strong>Stop doing 100s of sit-ups: I'm a personal trainer, and I use 'bear plank openers' to build strong abs and hips instead</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’m ditching my over-ear headphones for these Sony earbuds to beat the heat this summer — and they offer Bose-like sound for a lot less ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Over a year ago, <a href="https://www.tomsguide.com/audio/headphones/my-doctor-banned-me-from-earbuds-so-i-got-these-marshall-headphones-instead-and-im-surprised-i-didnt-do-it-sooner">I ditched in-ear buds for over-ear headphones</a> and didn't look back. You'll usually find me with my trusty <a href="https://www.tomsguide.com/audio/headphones/marshall-monitor-iii-review">Marshall Monitor III</a> headphones around my neck, or sitting atop my head while I jam to my favorite tunes. While I love my headphones, I'm no stranger to itchy ears from sweating, and due to the very nature of over-ear cans, they can be tricky to wear for long periods of time during the summer.</p><p>The solution is a simple one, of course: get a pair of in-ear buds. In-ear buds don't sit <em>around </em>your ears, like over-ear headphones do, so they don't feel suffocating during the year's hottest months. And you don't need to spend big bucks to get a decent pair — which is why I'm opting for the <a href="https://www.tomsguide.com/audio/earbuds/sony-wf-c710n-wireless-earbuds-review">Sony WF-C710N</a>.</p><div class="product"><a data-dimension112="96beb371-5880-4f00-b3a1-d1fb7fc50e85" data-action="Deal Block" data-label="WF-C710N" data-dimension48="WF-C710N" data-dimension25="$129" href="https://www.amazon.com/Sony-WF-C710N-Wireless-Noise-Canceling-Lightweight/dp/B0DWHB886R/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:970px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="ztmFvnegXZhqSbG5gSMaDD" name="Sony WF-C710N.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ztmFvnegXZhqSbG5gSMaDD-1920-80.jpg" mos="" align="middle" fullscreen="" width="970" height="546" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony WF-C710Ns build on the success of the WF-C700Ns, introducing touch controls, wear detection and new, stylish colors. The earbuds make basslines sound powerful and vocals sound clear, and a user-friendly app lets you customize the EQ to your heart’s content. Oh, and the ANC is extremely effective too — comparable to Bose but for a lot less money.<a class="view-deal button" href="https://www.amazon.com/Sony-WF-C710N-Wireless-Noise-Canceling-Lightweight/dp/B0DWHB886R/" target="_blank" rel="nofollow" data-dimension112="96beb371-5880-4f00-b3a1-d1fb7fc50e85" data-action="Deal Block" data-label="WF-C710N" data-dimension48="WF-C710N" data-dimension25="$129">View Deal</a></p></div><p>I tested the WF-C710N back in April 2025, and to date, they remain some of the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a> I've ever tested. They're lightweight and ridiculously comfortable, and their see-through Glass Blue design is nothing short of delicious. Meaty bass, crisp treble, refined mids, and with highly effective ANC — these earbuds have got it all. And the best part? They're $170 cheaper than the <a href="https://www.tomsguide.com/audio/earbuds/bose-quietcomfort-ultra-earbuds-2-review">Bose QuietComfort Ultra Gen 2</a>. It's time to <a href="https://www.tomsguide.com/uk/savings-squad">upgrade your life for less</a> with the WF-C710N.</p><h2 id="like-you-aren-t-even-wearing-them">Like you aren't even wearing them</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="WhjNQgYhrzmxFPh73WYTeb" name="Sony_WF_C710N_ 5.JPG" alt="A pair of Glass Blue Sony WF-C710N wireless earbuds." src="https://cdn.mos.cms.futurecdn.net/WhjNQgYhrzmxFPh73WYTeb-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Having tested and used many earbuds in my time, I believe that the true hallmark of a good pair is when you don't even realize they're in your ears. I should be able to go about my day without feeling earbuds digging into my ear canal. By that standard, then, the Sony WF-C710N are simply excellent.</p><p>Each earbud weighs just 0.18oz which is practically nothing. In my ears, they fit snugly and never once have they fallen out, even when I've been walking briskly or headbanging. There have been times where I've forgotten that the WF-C710N are in my ears, and that says everything you need to know about their comfort level.</p><p>In addition to that, the WF-C710N are sure to turn heads because they look stunning. Available in white, black and blue, I'd personally recommend the third option. You can see the wires and hardware on the see-through Glass Blue model, and this sets the earbuds apart from any others on the market right now.</p><h2 id="they-sound-amazing">They sound amazing</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="pPxCiV8PgTrFYW9jHpYkYb" name="Sony_WF_C710N_ 7.JPG" alt="A pair of Glass Blue Sony WF-C710N wireless earbuds." src="https://cdn.mos.cms.futurecdn.net/pPxCiV8PgTrFYW9jHpYkYb-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I would never recommend a pair of earbuds unless they sounded downright fantastic — yes, even if it's very comfortable and good-looking. The Sony WF-C710N have that covered, with their 5mm drivers delivering powerful bass and vibrant treble. Thanks to Sony's trademarked Digital Sound Enhancement Engine technology, sound is reproduced as close to the original recording as possible.</p><p>Through the WF-C710N, the distorted bass in 070 Shake's '<a href="https://open.spotify.com/track/4MH3D8angFt2z5B8yO3xnG?si=d3124f7427c24be1" target="_blank" rel="nofollow">Elephant</a>' and the reverberating bassline in Twenty One Pilots' '<a href="https://open.spotify.com/track/2OdfQjIJlomZFUBTEDKMre?si=01db031d2b594591" target="_blank" rel="nofollow">No Chances</a>' sound fantastic. It's powerful enough that you can you feel the music in your skull but not so booming that it overpowers the rest of the soundstage.</p><p>Treble is crisp but not overzealous, so you can clearly hear the high-pitched coins in Pink Floyd's '<a href="https://open.spotify.com/track/0vFOzaXqZHahrZp6enQwQb?si=fdeb27a68dbc4137" target="_blank" rel="nofollow">Money</a>' but that sound doesn't pierce through the soundscape or undermines the other instruments.</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="63NPVoRigVtMbYzNCmD6fb" name="Sony_WF_C710N_ 9.JPG" alt="A pair of Glass Blue Sony WF-C710N wireless earbuds." src="https://cdn.mos.cms.futurecdn.net/63NPVoRigVtMbYzNCmD6fb-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>For a pair of in-ear buds, the WF-C710N provide an excellent, immersive listening experience. Combine that with the highly effective ANC that goes toe-to-toe with Bose, and you've got a winning formula. The ANC is so good that you can't hear the kettle boiling or the TV indoors, and the noise of a bus rattling while you're traveling is also muted. You're getting bang for your buck here.</p><p>In the summer, you need a pair of earbuds that enable your ears to breathe — and while headphones are great, they clamp down on your earlobes, leading to some discomfort, especially during heatwaves. Conversely, when it gets colder, over-ear cans are a great choice, because they can double as earmuffs and provide some warmth to your ears when temperatures drop.</p><p>What do you think? Do you swap between over-ear headphones (or on-ears) and in-ear buds depending on the weather? I'd love to hear from you in the comments below!</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-egZ2pX"></div>                            </div>                            <script src="https://kwizly.com/embed/egZ2pX.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/audio/earbuds/sony-wf-c710n-wireless-earbuds-review">Sony WF-C710N review</a></li><li><a href="https://www.tomsguide.com/audio/headphones/bose-quietcomfort-ultra-2nd-gen-marshall-milton-anc-which-should-you-buy">Do you need to spend $449 on the Bose QuietComfort Ultra 2nd Gen when you can get the new Marshall Milton ANC for $229? I tested both and I’m very surprised</a></li><li><a href="https://www.tomsguide.com/audio/headphones/not-all-on-ear-headphones-are-uncomfortable-ive-been-using-the-new-marshall-milton-anc-for-3-weeks-and-theyre-ridiculously-comfortable-and-offer-outstanding-sound-too">Not all on-ear headphones are uncomfortable: I’ve been using the new Marshall Milton ANC for 3 weeks and they’re ridiculously comfortable and offer outstanding sound too</a></li></ul><div data-widget-type="review" data-model-name="Sony WF-C710N"></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/earbuds/im-ditching-my-over-ear-headphones-for-these-sony-in-ear-buds-to-beat-the-heat-this-summer-and-they-offer-bose-like-sound-for-a-lot-less</link>
                                                                            <description>
                            <![CDATA[ Over-ear headphones are my go-to but they make my ears too hot in the summer. The solution? A pair of fantastic in-ear buds that sound like Bose but for less! ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ccC8moiwRhLN5oHa9ShG94</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/jVTB8KoGy6KygtEaHNqpE3-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 23 May 2026 05:15:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Earbuds]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3-320-70.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&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/jVTB8KoGy6KygtEaHNqpE3-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sony WF-C710N in-ear buds]]></media:description>                                                            <media:text><![CDATA[Sony WF-C710N in-ear buds]]></media:text>
                                <media:title type="plain"><![CDATA[Sony WF-C710N in-ear buds]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/jVTB8KoGy6KygtEaHNqpE3-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Over a year ago, <a href="https://www.tomsguide.com/audio/headphones/my-doctor-banned-me-from-earbuds-so-i-got-these-marshall-headphones-instead-and-im-surprised-i-didnt-do-it-sooner">I ditched in-ear buds for over-ear headphones</a> and didn't look back. You'll usually find me with my trusty <a href="https://www.tomsguide.com/audio/headphones/marshall-monitor-iii-review">Marshall Monitor III</a> headphones around my neck, or sitting atop my head while I jam to my favorite tunes. While I love my headphones, I'm no stranger to itchy ears from sweating, and due to the very nature of over-ear cans, they can be tricky to wear for long periods of time during the summer.</p><p>The solution is a simple one, of course: get a pair of in-ear buds. In-ear buds don't sit <em>around </em>your ears, like over-ear headphones do, so they don't feel suffocating during the year's hottest months. And you don't need to spend big bucks to get a decent pair — which is why I'm opting for the <a href="https://www.tomsguide.com/audio/earbuds/sony-wf-c710n-wireless-earbuds-review">Sony WF-C710N</a>.</p><div class="product"><a data-dimension112="96beb371-5880-4f00-b3a1-d1fb7fc50e85" data-action="Deal Block" data-label="WF-C710N" data-dimension48="WF-C710N" data-dimension25="$129" href="https://www.amazon.com/Sony-WF-C710N-Wireless-Noise-Canceling-Lightweight/dp/B0DWHB886R/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:970px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="ztmFvnegXZhqSbG5gSMaDD" name="Sony WF-C710N.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ztmFvnegXZhqSbG5gSMaDD-1920-80.jpg" mos="" align="middle" fullscreen="" width="970" height="546" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony WF-C710Ns build on the success of the WF-C700Ns, introducing touch controls, wear detection and new, stylish colors. The earbuds make basslines sound powerful and vocals sound clear, and a user-friendly app lets you customize the EQ to your heart’s content. Oh, and the ANC is extremely effective too — comparable to Bose but for a lot less money.<a class="view-deal button" href="https://www.amazon.com/Sony-WF-C710N-Wireless-Noise-Canceling-Lightweight/dp/B0DWHB886R/" target="_blank" rel="nofollow" data-dimension112="96beb371-5880-4f00-b3a1-d1fb7fc50e85" data-action="Deal Block" data-label="WF-C710N" data-dimension48="WF-C710N" data-dimension25="$129">View Deal</a></p></div><p>I tested the WF-C710N back in April 2025, and to date, they remain some of the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a> I've ever tested. They're lightweight and ridiculously comfortable, and their see-through Glass Blue design is nothing short of delicious. Meaty bass, crisp treble, refined mids, and with highly effective ANC — these earbuds have got it all. And the best part? They're $170 cheaper than the <a href="https://www.tomsguide.com/audio/earbuds/bose-quietcomfort-ultra-earbuds-2-review">Bose QuietComfort Ultra Gen 2</a>. It's time to <a href="https://www.tomsguide.com/uk/savings-squad">upgrade your life for less</a> with the WF-C710N.</p><h2 id="like-you-aren-t-even-wearing-them">Like you aren't even wearing them</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="WhjNQgYhrzmxFPh73WYTeb" name="Sony_WF_C710N_ 5.JPG" alt="A pair of Glass Blue Sony WF-C710N wireless earbuds." src="https://cdn.mos.cms.futurecdn.net/WhjNQgYhrzmxFPh73WYTeb-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Having tested and used many earbuds in my time, I believe that the true hallmark of a good pair is when you don't even realize they're in your ears. I should be able to go about my day without feeling earbuds digging into my ear canal. By that standard, then, the Sony WF-C710N are simply excellent.</p><p>Each earbud weighs just 0.18oz which is practically nothing. In my ears, they fit snugly and never once have they fallen out, even when I've been walking briskly or headbanging. There have been times where I've forgotten that the WF-C710N are in my ears, and that says everything you need to know about their comfort level.</p><p>In addition to that, the WF-C710N are sure to turn heads because they look stunning. Available in white, black and blue, I'd personally recommend the third option. You can see the wires and hardware on the see-through Glass Blue model, and this sets the earbuds apart from any others on the market right now.</p><h2 id="they-sound-amazing">They sound amazing</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="pPxCiV8PgTrFYW9jHpYkYb" name="Sony_WF_C710N_ 7.JPG" alt="A pair of Glass Blue Sony WF-C710N wireless earbuds." src="https://cdn.mos.cms.futurecdn.net/pPxCiV8PgTrFYW9jHpYkYb-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I would never recommend a pair of earbuds unless they sounded downright fantastic — yes, even if it's very comfortable and good-looking. The Sony WF-C710N have that covered, with their 5mm drivers delivering powerful bass and vibrant treble. Thanks to Sony's trademarked Digital Sound Enhancement Engine technology, sound is reproduced as close to the original recording as possible.</p><p>Through the WF-C710N, the distorted bass in 070 Shake's '<a href="https://open.spotify.com/track/4MH3D8angFt2z5B8yO3xnG?si=d3124f7427c24be1" target="_blank" rel="nofollow">Elephant</a>' and the reverberating bassline in Twenty One Pilots' '<a href="https://open.spotify.com/track/2OdfQjIJlomZFUBTEDKMre?si=01db031d2b594591" target="_blank" rel="nofollow">No Chances</a>' sound fantastic. It's powerful enough that you can you feel the music in your skull but not so booming that it overpowers the rest of the soundstage.</p><p>Treble is crisp but not overzealous, so you can clearly hear the high-pitched coins in Pink Floyd's '<a href="https://open.spotify.com/track/0vFOzaXqZHahrZp6enQwQb?si=fdeb27a68dbc4137" target="_blank" rel="nofollow">Money</a>' but that sound doesn't pierce through the soundscape or undermines the other instruments.</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="63NPVoRigVtMbYzNCmD6fb" name="Sony_WF_C710N_ 9.JPG" alt="A pair of Glass Blue Sony WF-C710N wireless earbuds." src="https://cdn.mos.cms.futurecdn.net/63NPVoRigVtMbYzNCmD6fb-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>For a pair of in-ear buds, the WF-C710N provide an excellent, immersive listening experience. Combine that with the highly effective ANC that goes toe-to-toe with Bose, and you've got a winning formula. The ANC is so good that you can't hear the kettle boiling or the TV indoors, and the noise of a bus rattling while you're traveling is also muted. You're getting bang for your buck here.</p><p>In the summer, you need a pair of earbuds that enable your ears to breathe — and while headphones are great, they clamp down on your earlobes, leading to some discomfort, especially during heatwaves. Conversely, when it gets colder, over-ear cans are a great choice, because they can double as earmuffs and provide some warmth to your ears when temperatures drop.</p><p>What do you think? Do you swap between over-ear headphones (or on-ears) and in-ear buds depending on the weather? I'd love to hear from you in the comments below!</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-egZ2pX"></div>                            </div>                            <script src="https://kwizly.com/embed/egZ2pX.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/audio/earbuds/sony-wf-c710n-wireless-earbuds-review">Sony WF-C710N review</a></li><li><a href="https://www.tomsguide.com/audio/headphones/bose-quietcomfort-ultra-2nd-gen-marshall-milton-anc-which-should-you-buy">Do you need to spend $449 on the Bose QuietComfort Ultra 2nd Gen when you can get the new Marshall Milton ANC for $229? I tested both and I’m very surprised</a></li><li><a href="https://www.tomsguide.com/audio/headphones/not-all-on-ear-headphones-are-uncomfortable-ive-been-using-the-new-marshall-milton-anc-for-3-weeks-and-theyre-ridiculously-comfortable-and-offer-outstanding-sound-too">Not all on-ear headphones are uncomfortable: I’ve been using the new Marshall Milton ANC for 3 weeks and they’re ridiculously comfortable and offer outstanding sound too</a></li></ul><div data-widget-type="review" data-model-name="Sony WF-C710N"></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Forget fans, I've been testing Sony's new wearable thermo device — and it's the gadget of the future ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Reon Pocket Pro Plus is the most futuristic product I’ve tested yet (<em>still</em> waiting for that flying car). It’s essentially a personal air conditioning system that uses sensors, or your manual inputs, to regulate your body temperature. When worn, it sits at the top of your back and uses a stainless steel plate that warms and cools. </p><p>It has effective heating and cooling capabilities that are personally adjustable to you via an easy-to-use smartphone app, or with the on-board buttons — but these are a little hard to reach if you’re wearing the device on your back. </p><p>The decent battery life also means that it’s good for a full working day. So, to find out more about the device and see if it’s something you want to implement in your daily routine, keep reading my full Reon Pocket Pro Plus review.</p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-specs"><span>Reon Pocket Pro Plus review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.co.uk/REON-POCKET-Temperature-Lightweight-Comfortable-Light-Grey/dp/B0GTQ1FGVD" target="_blank" rel="nofollow">£199</a> (U.S. availability summer 2026)</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>6.9 x 4.9 x 2.4 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>9.1 ounces </p></td></tr><tr><td class="firstcol " ><p><strong>Operating temperature</strong></p></td><td  ><p>41ºF - 104ºF (5ºC - 40ºC)</p></td></tr><tr><td class="firstcol " ><p><strong>Supported features</strong></p></td><td  ><p>Warming and cooling</p></td></tr><tr><td class="firstcol " ><p><strong>Material</strong></p></td><td  ><p>Stainless steel</p></td></tr><tr><td class="firstcol " ><p><strong>Supported operating systems</strong></p></td><td  ><p>iOS 16 or later, Android 9 or later</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.0</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>10 hours</p></td></tr><tr><td class="firstcol " ><p><strong>Charging time</strong></p></td><td  ><p>200 minutes</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-price-availability"><span>Reon Pocket Pro Plus review: Price & availability</span></h2><p>The Reon Pocket Pro Plus is available to purchase in the U.K. for <a href="https://www.amazon.co.uk/REON-POCKET-Temperature-Lightweight-Comfortable-Light-Grey/dp/B0GTQ1FGVD" target="_blank" rel="nofollow">£199 from Amazon</a> or directly from <a href="https://www.sony.co.uk/store/product/rnpkp1pth.e/REON-Pocket-Pro-Plus" target="_blank" rel="nofollow">Sony’s online store</a>. Unfortunately, it’s not available for purchase in the U.S. at the time of writing, but it is expected to become available in summer 2026 and will only be available for purchase directly from the Sony store. You can <a href="https://electronics.sony.com/t/reonpocket" target="_blank">sign up for the mailing list</a> to be notified of availability. And summer can’t come quickly enough, because this is an ideal cooling solution that can carry you right through and keep you warm in the winter. </p><p>Previous models of the Reon include the Reon Pocket 5, which is available for £149, and the Reon Pocket Pro for £199, but the Pocket Pro Plus comes with performance and comfort upgrades. </p><p>If you’re looking for something that’s going to keep you cool in the summer, you could also consider the <a href="https://www.tomsguide.com/home/shark-just-launched-a-new-3-in-1-cooling-system-that-you-can-literally-wear-while-on-the-go">Shark ChillPill</a> for $149, which is a 3-in-1 cooling system that fits in your bag to go with you everywhere. Or alternatively, the <a href="https://www.tomsguide.com/home/dyson-just-launched-its-first-ever-portable-fan-and-it-could-be-the-gadget-of-the-summer">Dyson HushJet Mini Cool</a> ($99) is cute to hold with focused airflow and is actually pretty quiet. </p><p>And if you need to keep warm in the winter months, you’d need to look towards something like a space heater, or rechargeable hand warmers, like the <a href="https://www.tomsguide.com/home/my-gloves-suck-so-i-got-these-usd20-rechargeable-handwarmers-and-theyre-the-best-thing-ive-bought-this-year">Ocoopa UT4s</a> ($26). But ultimately, there isn’t anything like the Reon Pocket Pro Plus on the market. It’s in a league of its own when it comes to personal temperature regulation, providing both heating and cooling in a single package.</p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-design-comfort"><span>Reon Pocket Pro Plus review: Design & comfort</span></h2><p>The Reon Pocket Pro Plus looks a little like a Chinchou Pokémon. It has two antenna-like arms that come out of a long body and wrap around your neck, so the device sits securely on your back, running down your spine. </p><p>It measures 6.9 x 4.9 x 2.4 inches, so it doesn’t come too far down my back (I’m 5’4” for reference), and it’s slightly curved, so it follows the curvature from my neck to my back perfectly. After a while, I hardly noticed I was wearing it since it’s so lightweight at just 9.1 ounces. I was conscious about the arms that wrap around your neck at first, but much like the weight on my back, I got used to them quickly. It’s also worth noting that since the Reon sits on your back, you can’t wear it at night if you sleep on your back.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6FU6iwrv7XELRozAbxYDWA" name="Sony Reon Pocket Pro Plus" alt="Sony Reon Pocket Pro Plus photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/6FU6iwrv7XELRozAbxYDWA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The back of the device, where it touches your skin, is made of stainless steel. I’d recommend letting it heat up for a second before using, as this can be freezing cold given the material, and a bit of a shock to the system.</p><p>This device is worn under your clothes, so inevitably, it gives a slight hump under whatever top you’re wearing. But honestly, it’s not that noticeable. There is an air vent that comes out of the top of your shirt slightly so the device has airflow, and if you’re wearing a tee with a higher collar, you can swap it out for a longer one, thanks to the included interchangeable vents.</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="A23tSaSrb53jAN4KuJNHmA" name="Sony Reon Pocket Pro Plus" alt="Sony Reon Pocket Pro Plus worn on back" src="https://cdn.mos.cms.futurecdn.net/A23tSaSrb53jAN4KuJNHmA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Reon also comes with a tag to sense the surrounding environment for Smart Mode. This tag has an included carabiner so you can attach it to your belt or bag. I kept mine attached to my bag. It wirelessly connects to the device and helps it regulate the temperature based on the environment, so you don’t need to change the settings manually.</p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-controls-app"><span>Reon Pocket Pro Plus review: Controls & app</span></h2><p>The controls on the body of the Reon Pocket Pro Plus are easy to use. On the left side, there’s a stop/start button, and the mode button sits just below it to switch between cooling and heating. On the right-hand side is a plus/minus button for increasing and decreasing temperature, and a button for activating Smart Mode. I found these controls very responsive during use, but a little tricky to reach when the device was on my neck. </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:1510px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="UfKrJ2onVkimLSgvNvUAse" name="reon app" alt="Screenshots taken from the Reon pocket app of the controls on the Reon Pocket Pro Plus" src="https://cdn.mos.cms.futurecdn.net/UfKrJ2onVkimLSgvNvUAse-1920-80.jpg" mos="" align="middle" fullscreen="" width="1510" height="850" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony Reon Pocket app)</span></figcaption></figure><p>So, if you want to make changes when you’re already wearing the device, rather than taking it off your neck to make adjustments, you simply use the mobile app to turn your phone into a remote control. I downloaded the Reon Pocket app from the <a href="https://apps.apple.com/gb/app/reon-pocket/id1498749786" target="_blank">iOS App Store</a>, and it automatically connected to the device when I opened 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="AFz6P62ongdQdjbNiuvbZA" name="Sony Reon Pocket Pro Plus" alt="Sony Reon Pocket Pro Plus sensor" src="https://cdn.mos.cms.futurecdn.net/AFz6P62ongdQdjbNiuvbZA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Here, you can select warm or cool functions and then make adjustments to this temperature. I find this super useful as someone who works in an office with air conditioning that is always being adjusted by whoever walks past (I see you making those sneaky adjustments to the air). It means that I’ve been able to regulate my personal temperature easily. </p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-performance"><span>Reon Pocket Pro Plus review: Performance</span></h2><p>I’ve never used anything like the Reon Pocket Pro Plus before, but now, I’m not sure if I can use a regular handheld fan ever again. Most cooling fans make noise, although there are some silent ones on the market. And, as “handheld” suggests, need to be held in your hands, but the Reon ensures your hands are free at all times. The device operates between 41ºF and 104ºF (5ºC and 40ºC).</p><h3 id="cool">Cool</h3><p>When using the Cool setting on the Reon manually, you can choose from five temperatures. This gave me a lot of control and I could make adjustments from the app, so if it got too cold, I could turn the temperature up accordingly. </p><p>In the Cool section of the app, you can also use Smart Cool. This gives a target temperature based on the selected mode. The modes include Low Cool, Slightly Low Cool, Medium (which is the recommended mode), Slightly High Cool, and High Cool. The app notes that the battery life lasts the longest in Low Cool mode and delivers the quickest in High Cool. </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="nvf49sybCPxVoDcLboFUJA" name="Sony Reon Pocket Pro Plus" alt="control buttons of the Sony Reon Pocket Pro Plus" src="https://cdn.mos.cms.futurecdn.net/nvf49sybCPxVoDcLboFUJA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The response to changes in the settings was immediate, and there was no sound from the vent of the device. It kept me cool even when the heating was on in my house, and helped to regulate my entire body temperature despite just being at the top of my back. Obviously, everyone’s body is different when regulating temperature, but I could feel the temperature adjustments I was making instantly. This was a lot faster than using a handheld portable fan.</p><h3 id="warm">Warm</h3><p>I was skeptical about the Warm mode but it worked perfectly. As someone who generally runs cold, I’m always eager for the heat to be turned up. But I can appreciate that not everybody has my nervous system’s affinity for the heat, so by using the Reon, I had my own personal central heating system. </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="EWMR4DjTQNA4sQKZLDCkGA" name="Sony Reon Pocket Pro Plus" alt="swappable vent of the Sony Reon Pocket Pro Plus" src="https://cdn.mos.cms.futurecdn.net/EWMR4DjTQNA4sQKZLDCkGA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Just like the Cool settings, you can use Smart Warm for heat. The smart mode setting works pretty much like a thermostat. You tell it the setting you want, and it works using the sensing tag that I talked about earlier to keep the temperature within range. This worked well, and I didn’t find myself reaching for my phone to change the heat or cooling settings in the app. </p><p>Again, like the cool system, the heat was instant. I was impressed by how quickly I felt the warmth throughout my body — it literally took a matter of seconds.</p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-battery-life"><span>Reon Pocket Pro Plus review: Battery life</span></h2><p>The Reon Pocket Pro Plus’ battery life depends heavily on the settings being used. The device is rated for 10 hours, but I found that the battery drained more quickly if I was using the heat settings compared to the cooling mode. And if you’re using either mode’s highest settings, it will drain faster. </p><p>10 hours of battery life should last you for the majority of your day. The average working day with the commute included is about 10.5 hours, so this amount of battery is enough to carry you through the hot subway and the chilly office air conditioning. </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="MyB7LtNkwnRcDxYEL5XvPA" name="Sony Reon Pocket Pro Plus" alt="charging port of the Sony Reon Pocket Pro Plus" src="https://cdn.mos.cms.futurecdn.net/MyB7LtNkwnRcDxYEL5XvPA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>This is a decent amount of battery life for a personal cooling device, as the Shark ChillPill has nine and a half hours of battery at its highest setting, and the Dyson HushJet Mini Cool delivers up to six hours on the lowest setting. </p><p>Charging of the Reon takes 130 minutes to reach 90%, and getting to 100% takes a further 70 minutes — so just over three hours in total.</p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-verdict"><span>Reon Pocket Pro Plus review: Verdict</span></h2><p>There’s nothing quite like the Reon Pocket Pro Plus on the market at the time of writing. It’s not a fan, and it’s not a heater. It’s more like a personalizable temperature regulatory device, and it’s given me effective heating and cooling results depending on my environment at the click of a button. </p><p>I like how fast the device responds to any changes I make within the user-friendly smartphone app. The Smart Cool and Smart Warm modes also make the device easy to use, as I didn’t need to go back and forth to constantly adjust the temperature while wearing it. The battery life is enough to get you through a full working day too, which I appreciate.</p><p>My only complaint would be that the buttons on the device itself are hard to use when you’re wearing it on your back. But since your phone can double as a remote, this is pretty much a non-issue. </p><p>I’m pretty sure we’re going to see everyone in big cities wearing these nifty devices before long — it’s way <em>cooler</em> than a flying car.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/home/reon-pocket-pro-plus-review</link>
                                                                            <description>
                            <![CDATA[ The Reon Pocket Pro Plus is a gadget in its own category that provides personalized heating and cooling discreetly under your clothing. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">vVBWmZYyGZHaQEsjHDWKtj</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/6n9psDKVB6ZTaSQ8krWBhA-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 22 May 2026 10:32:38 +0000</pubDate>                                                                                                                                <updated>Tue, 09 Jun 2026 10:32:38 +0000</updated>
                                                                                                                                            <category><![CDATA[Home]]></category>
                                                                                                <author><![CDATA[ ashley.thieme@futurenet.com (Ashley Thieme) ]]></author>                    <dc:creator><![CDATA[ Ashley Thieme ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3AWovHjApwuNrSGRS6WBcL-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ashley Thieme is a staff writer on the Reviews team at Tom’s Guide where she tests out the latest tech so you can know what’s going to be worth your time and money. She has a master’s degree in Magazine Journalism and a bachelor’s degree in Journalism, Media and Sociology from Cardiff University. She has bylines in titles including Women’s Health UK, sharing the latest health and wellness news stories, and Virgin Radio UK, providing the latest entertainment news and working on celebrity interviews. She has experience reporting on a variety of topics including music, literature, motorsport, entertainment and health. In previously published work, she has reviewed live music events, books, and wellness products. She values the importance of tech enhancing your life rather than taking over, and as a music fanatic, she is always looking for the best way to listen to new music releases. Discovering the top audio equipment that enhances sound quality and provides optimum comfort is what Ashley does best. In her spare time, Ashley enjoys hitting her reading goals on Goodreads by getting into the latest novels with a cup of tea as well as getting out in the Welsh mountains for a good hike on the weekend.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/6n9psDKVB6ZTaSQ8krWBhA-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sony Reon Pocket Pro Plus and sensor photographed on a gray background]]></media:description>                                                            <media:text><![CDATA[Sony Reon Pocket Pro Plus and sensor photographed on a gray background]]></media:text>
                                <media:title type="plain"><![CDATA[Sony Reon Pocket Pro Plus and sensor photographed on a gray background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/6n9psDKVB6ZTaSQ8krWBhA-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Reon Pocket Pro Plus is the most futuristic product I’ve tested yet (<em>still</em> waiting for that flying car). It’s essentially a personal air conditioning system that uses sensors, or your manual inputs, to regulate your body temperature. When worn, it sits at the top of your back and uses a stainless steel plate that warms and cools. </p><p>It has effective heating and cooling capabilities that are personally adjustable to you via an easy-to-use smartphone app, or with the on-board buttons — but these are a little hard to reach if you’re wearing the device on your back. </p><p>The decent battery life also means that it’s good for a full working day. So, to find out more about the device and see if it’s something you want to implement in your daily routine, keep reading my full Reon Pocket Pro Plus review.</p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-specs"><span>Reon Pocket Pro Plus review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.co.uk/REON-POCKET-Temperature-Lightweight-Comfortable-Light-Grey/dp/B0GTQ1FGVD" target="_blank" rel="nofollow">£199</a> (U.S. availability summer 2026)</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>6.9 x 4.9 x 2.4 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>9.1 ounces </p></td></tr><tr><td class="firstcol " ><p><strong>Operating temperature</strong></p></td><td  ><p>41ºF - 104ºF (5ºC - 40ºC)</p></td></tr><tr><td class="firstcol " ><p><strong>Supported features</strong></p></td><td  ><p>Warming and cooling</p></td></tr><tr><td class="firstcol " ><p><strong>Material</strong></p></td><td  ><p>Stainless steel</p></td></tr><tr><td class="firstcol " ><p><strong>Supported operating systems</strong></p></td><td  ><p>iOS 16 or later, Android 9 or later</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.0</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>10 hours</p></td></tr><tr><td class="firstcol " ><p><strong>Charging time</strong></p></td><td  ><p>200 minutes</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-price-availability"><span>Reon Pocket Pro Plus review: Price & availability</span></h2><p>The Reon Pocket Pro Plus is available to purchase in the U.K. for <a href="https://www.amazon.co.uk/REON-POCKET-Temperature-Lightweight-Comfortable-Light-Grey/dp/B0GTQ1FGVD" target="_blank" rel="nofollow">£199 from Amazon</a> or directly from <a href="https://www.sony.co.uk/store/product/rnpkp1pth.e/REON-Pocket-Pro-Plus" target="_blank" rel="nofollow">Sony’s online store</a>. Unfortunately, it’s not available for purchase in the U.S. at the time of writing, but it is expected to become available in summer 2026 and will only be available for purchase directly from the Sony store. You can <a href="https://electronics.sony.com/t/reonpocket" target="_blank">sign up for the mailing list</a> to be notified of availability. And summer can’t come quickly enough, because this is an ideal cooling solution that can carry you right through and keep you warm in the winter. </p><p>Previous models of the Reon include the Reon Pocket 5, which is available for £149, and the Reon Pocket Pro for £199, but the Pocket Pro Plus comes with performance and comfort upgrades. </p><p>If you’re looking for something that’s going to keep you cool in the summer, you could also consider the <a href="https://www.tomsguide.com/home/shark-just-launched-a-new-3-in-1-cooling-system-that-you-can-literally-wear-while-on-the-go">Shark ChillPill</a> for $149, which is a 3-in-1 cooling system that fits in your bag to go with you everywhere. Or alternatively, the <a href="https://www.tomsguide.com/home/dyson-just-launched-its-first-ever-portable-fan-and-it-could-be-the-gadget-of-the-summer">Dyson HushJet Mini Cool</a> ($99) is cute to hold with focused airflow and is actually pretty quiet. </p><p>And if you need to keep warm in the winter months, you’d need to look towards something like a space heater, or rechargeable hand warmers, like the <a href="https://www.tomsguide.com/home/my-gloves-suck-so-i-got-these-usd20-rechargeable-handwarmers-and-theyre-the-best-thing-ive-bought-this-year">Ocoopa UT4s</a> ($26). But ultimately, there isn’t anything like the Reon Pocket Pro Plus on the market. It’s in a league of its own when it comes to personal temperature regulation, providing both heating and cooling in a single package.</p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-design-comfort"><span>Reon Pocket Pro Plus review: Design & comfort</span></h2><p>The Reon Pocket Pro Plus looks a little like a Chinchou Pokémon. It has two antenna-like arms that come out of a long body and wrap around your neck, so the device sits securely on your back, running down your spine. </p><p>It measures 6.9 x 4.9 x 2.4 inches, so it doesn’t come too far down my back (I’m 5’4” for reference), and it’s slightly curved, so it follows the curvature from my neck to my back perfectly. After a while, I hardly noticed I was wearing it since it’s so lightweight at just 9.1 ounces. I was conscious about the arms that wrap around your neck at first, but much like the weight on my back, I got used to them quickly. It’s also worth noting that since the Reon sits on your back, you can’t wear it at night if you sleep on your back.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6FU6iwrv7XELRozAbxYDWA" name="Sony Reon Pocket Pro Plus" alt="Sony Reon Pocket Pro Plus photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/6FU6iwrv7XELRozAbxYDWA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The back of the device, where it touches your skin, is made of stainless steel. I’d recommend letting it heat up for a second before using, as this can be freezing cold given the material, and a bit of a shock to the system.</p><p>This device is worn under your clothes, so inevitably, it gives a slight hump under whatever top you’re wearing. But honestly, it’s not that noticeable. There is an air vent that comes out of the top of your shirt slightly so the device has airflow, and if you’re wearing a tee with a higher collar, you can swap it out for a longer one, thanks to the included interchangeable vents.</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="A23tSaSrb53jAN4KuJNHmA" name="Sony Reon Pocket Pro Plus" alt="Sony Reon Pocket Pro Plus worn on back" src="https://cdn.mos.cms.futurecdn.net/A23tSaSrb53jAN4KuJNHmA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Reon also comes with a tag to sense the surrounding environment for Smart Mode. This tag has an included carabiner so you can attach it to your belt or bag. I kept mine attached to my bag. It wirelessly connects to the device and helps it regulate the temperature based on the environment, so you don’t need to change the settings manually.</p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-controls-app"><span>Reon Pocket Pro Plus review: Controls & app</span></h2><p>The controls on the body of the Reon Pocket Pro Plus are easy to use. On the left side, there’s a stop/start button, and the mode button sits just below it to switch between cooling and heating. On the right-hand side is a plus/minus button for increasing and decreasing temperature, and a button for activating Smart Mode. I found these controls very responsive during use, but a little tricky to reach when the device was on my neck. </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:1510px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="UfKrJ2onVkimLSgvNvUAse" name="reon app" alt="Screenshots taken from the Reon pocket app of the controls on the Reon Pocket Pro Plus" src="https://cdn.mos.cms.futurecdn.net/UfKrJ2onVkimLSgvNvUAse-1920-80.jpg" mos="" align="middle" fullscreen="" width="1510" height="850" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony Reon Pocket app)</span></figcaption></figure><p>So, if you want to make changes when you’re already wearing the device, rather than taking it off your neck to make adjustments, you simply use the mobile app to turn your phone into a remote control. I downloaded the Reon Pocket app from the <a href="https://apps.apple.com/gb/app/reon-pocket/id1498749786" target="_blank">iOS App Store</a>, and it automatically connected to the device when I opened 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="AFz6P62ongdQdjbNiuvbZA" name="Sony Reon Pocket Pro Plus" alt="Sony Reon Pocket Pro Plus sensor" src="https://cdn.mos.cms.futurecdn.net/AFz6P62ongdQdjbNiuvbZA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Here, you can select warm or cool functions and then make adjustments to this temperature. I find this super useful as someone who works in an office with air conditioning that is always being adjusted by whoever walks past (I see you making those sneaky adjustments to the air). It means that I’ve been able to regulate my personal temperature easily. </p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-performance"><span>Reon Pocket Pro Plus review: Performance</span></h2><p>I’ve never used anything like the Reon Pocket Pro Plus before, but now, I’m not sure if I can use a regular handheld fan ever again. Most cooling fans make noise, although there are some silent ones on the market. And, as “handheld” suggests, need to be held in your hands, but the Reon ensures your hands are free at all times. The device operates between 41ºF and 104ºF (5ºC and 40ºC).</p><h3 id="cool">Cool</h3><p>When using the Cool setting on the Reon manually, you can choose from five temperatures. This gave me a lot of control and I could make adjustments from the app, so if it got too cold, I could turn the temperature up accordingly. </p><p>In the Cool section of the app, you can also use Smart Cool. This gives a target temperature based on the selected mode. The modes include Low Cool, Slightly Low Cool, Medium (which is the recommended mode), Slightly High Cool, and High Cool. The app notes that the battery life lasts the longest in Low Cool mode and delivers the quickest in High Cool. </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="nvf49sybCPxVoDcLboFUJA" name="Sony Reon Pocket Pro Plus" alt="control buttons of the Sony Reon Pocket Pro Plus" src="https://cdn.mos.cms.futurecdn.net/nvf49sybCPxVoDcLboFUJA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The response to changes in the settings was immediate, and there was no sound from the vent of the device. It kept me cool even when the heating was on in my house, and helped to regulate my entire body temperature despite just being at the top of my back. Obviously, everyone’s body is different when regulating temperature, but I could feel the temperature adjustments I was making instantly. This was a lot faster than using a handheld portable fan.</p><h3 id="warm">Warm</h3><p>I was skeptical about the Warm mode but it worked perfectly. As someone who generally runs cold, I’m always eager for the heat to be turned up. But I can appreciate that not everybody has my nervous system’s affinity for the heat, so by using the Reon, I had my own personal central heating system. </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="EWMR4DjTQNA4sQKZLDCkGA" name="Sony Reon Pocket Pro Plus" alt="swappable vent of the Sony Reon Pocket Pro Plus" src="https://cdn.mos.cms.futurecdn.net/EWMR4DjTQNA4sQKZLDCkGA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Just like the Cool settings, you can use Smart Warm for heat. The smart mode setting works pretty much like a thermostat. You tell it the setting you want, and it works using the sensing tag that I talked about earlier to keep the temperature within range. This worked well, and I didn’t find myself reaching for my phone to change the heat or cooling settings in the app. </p><p>Again, like the cool system, the heat was instant. I was impressed by how quickly I felt the warmth throughout my body — it literally took a matter of seconds.</p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-battery-life"><span>Reon Pocket Pro Plus review: Battery life</span></h2><p>The Reon Pocket Pro Plus’ battery life depends heavily on the settings being used. The device is rated for 10 hours, but I found that the battery drained more quickly if I was using the heat settings compared to the cooling mode. And if you’re using either mode’s highest settings, it will drain faster. </p><p>10 hours of battery life should last you for the majority of your day. The average working day with the commute included is about 10.5 hours, so this amount of battery is enough to carry you through the hot subway and the chilly office air conditioning. </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="MyB7LtNkwnRcDxYEL5XvPA" name="Sony Reon Pocket Pro Plus" alt="charging port of the Sony Reon Pocket Pro Plus" src="https://cdn.mos.cms.futurecdn.net/MyB7LtNkwnRcDxYEL5XvPA-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>This is a decent amount of battery life for a personal cooling device, as the Shark ChillPill has nine and a half hours of battery at its highest setting, and the Dyson HushJet Mini Cool delivers up to six hours on the lowest setting. </p><p>Charging of the Reon takes 130 minutes to reach 90%, and getting to 100% takes a further 70 minutes — so just over three hours in total.</p><h2 class="article-body__section" id="section-reon-pocket-pro-plus-review-verdict"><span>Reon Pocket Pro Plus review: Verdict</span></h2><p>There’s nothing quite like the Reon Pocket Pro Plus on the market at the time of writing. It’s not a fan, and it’s not a heater. It’s more like a personalizable temperature regulatory device, and it’s given me effective heating and cooling results depending on my environment at the click of a button. </p><p>I like how fast the device responds to any changes I make within the user-friendly smartphone app. The Smart Cool and Smart Warm modes also make the device easy to use, as I didn’t need to go back and forth to constantly adjust the temperature while wearing it. The battery life is enough to get you through a full working day too, which I appreciate.</p><p>My only complaint would be that the buttons on the device itself are hard to use when you’re wearing it on your back. But since your phone can double as a remote, this is pretty much a non-issue. </p><p>I’m pretty sure we’re going to see everyone in big cities wearing these nifty devices before long — it’s way <em>cooler</em> than a flying car.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested Sony 1000X The ColleXion vs Apple AirPods Max 2 — here's the winner ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Sony's had something up its sleeve for weeks, and that something is finally here. Ever since <a href="https://www.reddit.com/r/headphones/comments/1t34bty/sony_1000xx_the_collexion_spotted_worn_by_damson/" target="_blank" rel="nofollow">paparazzi photos of an actor donning suspicious new headphones leaked</a>, the audio world's been holding its breath — but hold your breath no more. To celebrate the 10th anniversary of the XM line, the Japanese everything-brand is releasing its first uber-premium over-ear headphone: 1000X The ColleXion. </p><p>1000X The ColleXion is less aimed at the <a href="https://www.tomsguide.com/reviews/bose-quietcomfort-headphones">Bose QuietComfort</a> (not Ultra) crowd and the JBL Tour One M3 Pro crowd, and more at the <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review">AirPods Max 2</a>, <a href="https://www.tomsguide.com/audio/over-ear-headphones/i-just-tested-these-impeccable-hi-res-headphones-and-they-blow-away-the-airpods-max">Bowers and Wilkins Px8 S2</a> crowd. But how do these "premium" headphones actually compare to said competition? </p><p>To find out how 1000X The ColleXion <em>really</em> fares against the AirPods Max 2, I put the two flagship cans through a series of tests. Lock in, because it's about to move up a gear. Will the Sony 1000X The ColleXion or the AirPods Max 2 win? </p>        <div class="featured_product_block featured_block_versus" data-id="eefbffc2-718b-414b-91b5-04d40dd9db1c">            <a href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS6F226/" data-model-name="AirPods Max 2" 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/TSi2XMUrFhYn5nqrGBsd59.jpg" alt="the airpods max 2 on a white background showing all the color options"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                            <div class='featured__brand'>Apple</div>                    <div class="featured__title">AirPods Max 2</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>The Apple AirPods Max 2 are Apple's flagship over-ears, with seamless Apple ecosystem integration, premium sound, and classic Apple design. </p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_versus" data-id="a7fab935-a18e-4cd3-a8dd-05208599d50e">            <a href="https://www.amazon.com/Sony-COLLEXION-Cancelling-Headphones-Microphones/dp/B0GW9MBZJ9" data-model-name="1000X The Collexion" 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/MsaMjcX9k2kWR6V2YBTq9h.png" alt="the sony 1000x the collexion deal block image"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                            <div class='featured__brand'>Sony</div>                    <div class="featured__title">1000X The Collexion</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>The 1000X The ColleXion are Sony's newest flagship headphones, combining premium materials and design with an even better sound profile. </p></p>                </div>                            </div>        </div><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-specs-compared"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Specs compared</span></h2><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Apple AirPods Max 2</strong></p></td><td  ><p><strong>Sony 1000X The Collexion</strong></p></td></tr><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS6F226/" target="_blank" rel="nofollow">$549</a> / <a href="https://www.amazon.co.uk/Apple-Headphones-Cancellation-Personalised-Translation/dp/B0GSS66JW8" target="_blank" rel="nofollow">£499</a></p></td><td  ><p><a href="https://www.amazon.com/Sony-COLLEXION-Cancelling-Headphones-Microphones/dp/B0GW9MBZJ9" target="_blank" rel="nofollow">$650 </a>/ <a href="https://www.amazon.co.uk/COLLEXION-Bluetooth-Headphones-Studio-Grade-Cancelling-Black/dp/B0GY4PPX7F" target="_blank" rel="nofollow">£549</a></p></td></tr><tr><td class="firstcol " ><p><strong>Headphone style</strong></p></td><td  ><p>Closed back</p></td><td  ><p>Closed back</p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>40mm dynamic</p></td><td  ><p>30mm dynamic</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz – 20KHz</p></td><td  ><p>20 Hz – 20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Impedance</strong></p></td><td  ><p>32Ω</p></td><td  ><p>24Ω (Passive) / 32Ω (Active)</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>No IP rating</p></td><td  ><p>No IP rating</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.3, USB-C wired</p></td><td  ><p>Bluetooth 6.0, LDAC, 3.5mm wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>Yes </p></td><td  ><p>Yes, up to 2 devices</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>Up to 20 hours (ANC on)</p></td><td  ><p>24 hours (ANC on)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>13.6 ounces</p></td><td  ><p>11.3 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Midnight, Starlight, Blue, Purple, Orange</p></td><td  ><p>Black, White</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-price"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Price</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="2aDfyW37Szj5dCTPo8QzPF" name="Sony-WH-1000-The-Collexion-7" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/2aDfyW37Szj5dCTPo8QzPF-1920-80.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 Sony 1000X The ColleXion are an eye-watering $659 at their May 2026 launch, while the AirPods Max 2 launched in April 2026 for $549. </p><p>You're paying $110 extra for The ColleXion — and the AirPods Max 2 were already outrageously expensive. I'm not too sure what this extra $100 is getting you — sure, The ColleXion look nice and are much more premium than the WH-1000XM6, but still, $650 is a bit of a stretch. </p><p><strong>Winner: </strong>AirPods Max 2 </p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-design"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: 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="qByoRqBT5yHC3DPHjNGi2F" name="AirpodsMax2_04.JPG" alt="the apple airpods max 2 in blue photographed against a blue tom's guide background, showing the mesh headband, anodized aluminum ear cups, carry case, and controls" src="https://cdn.mos.cms.futurecdn.net/qByoRqBT5yHC3DPHjNGi2F-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Right, so the AirPods Max 2 and the 1000X The ColleXion both serve a similar (unfortunate) purpose: being a fashion statement. Headphones <em>should</em> have one job — playing music — but Apple and Sony seem to be trying to make over-ears something you wear for the sole purpose of wearing them. </p><p>They have both kind of... succeeded, though? The AirPods Max 2 and ColleXion both look gorgeous, with aluminum and stainless steel finishes that scream premium. I suppose, if you're paying $550-$650 for headphones, you'd expect them to look good. Personally, I still think the AirPods Max 2 look better, but I know beauty is in the eye of the beholder, so it's fine if you disagree there.</p><p>The AirPods Max 2 have much more intuitive controls — no tapping randomly on the ear cups — but The ColleXion now have a Spatial Upmixer button, so you can cycle through music upmix and cinema upmix without the Sony Headphones app. I personally did not enjoy how the Spatial Upmixer made my music super tinny, so I never used this. </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="pdw8ak4ZDABxE6JqwA294Y" name="Sony-WH-1000-The-Collexion-2-LIST2" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/pdw8ak4ZDABxE6JqwA294Y-1920-80.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>However, while the AirPods Max 2 win in terms of design and controls, The ColleXion win in terms of carry case. The AirPods Max 2 have no case worth talking about, but The ColleXion have a pretty awesome hard shell mini handbag case. Not only does this protect the headphones, it also looks pretty stylish. As a result, I'd say this round is a draw.</p><p><strong>Winner: </strong>Draw. </p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-sound-quality"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Sound quality</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="n68Fe55iVgfGoUDwyXCN8F" name="AirpodsMax2_06.JPG" alt="the apple airpods max 2 in blue photographed against a blue tom's guide background, showing the mesh headband, anodized aluminum ear cups, carry case, and controls" src="https://cdn.mos.cms.futurecdn.net/n68Fe55iVgfGoUDwyXCN8F-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Both the 1000XX The ColleXion and the AirPods Max 2 sound divine. If you like expansive, melodic headphones capable of delicately balancing rich bass and subtle detail, you'll like both the Sony and Apple offerings. </p><p>To accurately compare the sound, I listened to both The ColleXion and the AirPods Max 2 as my daily drivers, playing every genre under the sun (including podcasts and YouTube videos). I listened to everything on Qobuz, the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for audiophiles.</p><p>'Neverender' by Justice & Tame Impala is an electronic track with an intense synth-heavy instrumental and infectious metallic percussion. Neither the ColleXion nor the AirPods Max 2 lost any detail, and the vocals had unbeatable clarity, even during heavier sections. </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="kgxmAATBMgwosMoz4rRorb" name="Sony-WH-1000-The-Collexion-3" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/kgxmAATBMgwosMoz4rRorb-1920-80.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>When I played a heavier rock track, 'RIDING WITH MY GIRLS' by Die Spitz, the ColleXion handled the crunchy bass and metal-tinged guitars well. I wanted a touch more depth to the percussion — the cymbals kind of faded into the background — but in general, the ColleXion performed well. </p><p>When I listened with the AirPods Max 2, the details were louder and pulled forward, including the cymbals and crunchy guitar effects. Neither headphones managed to make the bass sound particularly excellent, but the AirPods Max 2 created a livelier soundscape. </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="AXejktPxeoGE9bhCYDCfvE" name="AirpodsMax2_05.JPG" alt="the apple airpods max 2 in blue photographed against a blue tom's guide background, showing the mesh headband, anodized aluminum ear cups, carry case, and controls" src="https://cdn.mos.cms.futurecdn.net/AXejktPxeoGE9bhCYDCfvE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Of course, I also had to test with LDAC, Sony's proprietary hi-res Bluetooth codec. 'Parachute' by Hayley Williams is a voice-led guitar track with heavy, almost over-processed bass and emotive vocals. Williams' voice sounded particularly incredible with LDAC enabled. </p><p>With the AirPods Max 2, 'Parachute' sounded clear, detailed, and had vibrant bass. Williams' voice was, again, front and center, weaving confidently between the over-processed bass and percussion. </p><p>The AirPods Max 2 <em>just</em> beat The ColleXion here — the ColleXion handle higher frequencies really well, but the bass was a bit too unrefined. </p><p>Both the AirPods Max 2 and the Sony 1000X The ColleXion have wired playback — USB-C and 3.5mm aux, respectively — which provides similarly lossless performances. Naturally, the AirPods Max 2 sound best via Apple Music and USB-C, and the Sonys sound best via Qobuz and 3.5mm wired. </p><p>Overall, both headphones sound great, and I'm sure you wouldn't be disappointed with either. The AirPods <em>just</em> take this round due to the more balanced bass response, though, as The ColleXion's bass can verge on unrefined in heavier genres. </p><p><strong>Winner: </strong>Apple<strong> </strong>AirPods Max 2</p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-anc"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: ANC</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="5qQPjACo9Hn4vHeQ7hedik" name="Sony-WH-1000-The-Collexion-6" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/5qQPjACo9Hn4vHeQ7hedik-1920-80.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>Both the AirPods Max 2 and The ColleXion have fantastic ANC, but the Sonys are the better performers here. During tests, I couldn't hear <em>anything</em> through the The ColleXion, not even high-frequency noises that often leak through even the strongest ANC, but I could hear things like clacking keyboards and chest-vibrating construction noises when listening to the AirPods Max 2. </p><p>For everyday use, though, you would get on just fine with either. I predominantly use my AirPods Max 2 and the ANC is everything I could need. </p><p><strong>Winner: </strong>Sony 1000X The ColleXion</p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-call-quality"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Call quality</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="YRAfCA4JGrzcNbkv4dXjTL" name="Sony-WH-1000-The-Collexion-14 (2)" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/YRAfCA4JGrzcNbkv4dXjTL-1920-80.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've said it before (and I'm sure I'll say it again), but we've reached a point in the Bluetooth headphone life cycle where pretty much every single flagship over-ears will provide you crystal-clear sound. </p><p>I have only good things to say about the AirPods Max 2 and Sony 1000X The ColleXion's call quality. Both headphones have crystal-clear audio, no fuzziness or hissing in sibilant words. Both headphones reduce ambient noise wonderfully, so I have no reservations about recommending either headphones for calls in loud or quiet environments. </p><p><strong>Winner: </strong>Draw. </p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-battery"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Battery</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="kcaAzw9zQYHJnoRKA7d5DS" name="Sony-WH-1000-The-Collexion-1-LIST" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/kcaAzw9zQYHJnoRKA7d5DS-1920-80.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 battery life is my biggest qualm with the AirPods Max 2. I can't quite believe Apple released headphones with a 20-hour battery life in 2026, but here we are. While The ColleXion have a 24-hour battery, this still isn't particularly impressive, either. </p><p>It's better, don't get me wrong — I'll take everything I can get as long as it's better than the AirPods Max 2 — but when the Sony WH-1000XM6 have a 30-hour battery life for $200 cheaper, this is a huge miss from Sony. </p><p>Of course, the Sonys win by default, but really, there are no winners here. </p><p><strong>Winner: </strong>Sony 1000X The ColleXion</p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-verdict"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: 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="SFZsRLkZwDr2CuvgqXtCEF" name="AirpodsMax2_07.JPG" alt="the apple airpods max 2 in blue photographed against a blue tom's guide background, showing the mesh headband, anodized aluminum ear cups, carry case, and controls" src="https://cdn.mos.cms.futurecdn.net/SFZsRLkZwDr2CuvgqXtCEF-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Both the AirPods Max 2 and the Sony 1000X The ColleXion received 85 points, making this versus a draw. You won't be disappointed by either the AirPods Max 2 or The ColleXion. </p><p>It's near impossible to choose between these two headphones. Both ooze style, have premium sound quality, and an array of impressive features (although I disliked Sony's Spatial Upmixer, you might love it). Both earn their place as some of the most premium, <a href="https://www.tomsguide.com/us/best-wireless-headphones,review-5565.html">best wireless headphones</a> money can buy. </p><p>I would personally choose the Apple AirPods Max 2, but The ColleXion still have a lot going for them: Want lighter cans, don't mind a more unrefined bass, and love Sony's impeccable ANC? Go for the 1000X The ColleXion. If you value metal construction, vivacious sound, and don't mind a poorer battery life, the AirPods will treat you right. </p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-ratings-scorecard"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Ratings scorecard</span></h2><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>AirPods Max 2</strong></p></th><th  ><p><strong>Sony 1000XX The ColleXion</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Price (5)</strong></p></td><td  ><p>3</p></td><td  ><p>2</p></td></tr><tr><td class="firstcol " ><p><strong>Design & controls (15)</strong></p></td><td  ><p>13</p></td><td  ><p>13</p></td></tr><tr><td class="firstcol " ><p><strong>Features (10)</strong></p></td><td  ><p>10</p></td><td  ><p>9</p></td></tr><tr><td class="firstcol " ><p><strong>Sound quality (25)</strong></p></td><td  ><p>25</p></td><td  ><p>24</p></td></tr><tr><td class="firstcol " ><p><strong>ANC (20)</strong></p></td><td  ><p>17</p></td><td  ><p>18</p></td></tr><tr><td class="firstcol " ><p><strong>Call quality (10)</strong></p></td><td  ><p>9</p></td><td  ><p>9</p></td></tr><tr><td class="firstcol " ><p><strong>Battery (15)</strong></p></td><td  ><p>8</p></td><td  ><p>10</p></td></tr><tr><td class="firstcol " ><p><strong>Total score (100)</strong></p></td><td  ><p>85</p></td><td  ><p>85</p></td></tr></tbody></table></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-vs-sony-1000x-the-collexion</link>
                                                                            <description>
                            <![CDATA[ I tested the Apple AirPods Max 2 versus the Sony 1000X The ColleXion, and this is the one I'd buy. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">zJcMSQMBaLsepAxzrXkejk</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/EsKsqEVsFK2YbjcXauk29T-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 22 May 2026 10:30:00 +0000</pubDate>                                                                                                                                <updated>Fri, 22 May 2026 11:46:36 +0000</updated>
                                                                                                                                            <category><![CDATA[Headphones]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/EsKsqEVsFK2YbjcXauk29T-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[a photo of the airpods max 2 and the sony 1000x the collexion]]></media:description>                                                            <media:text><![CDATA[a photo of the airpods max 2 and the sony 1000x the collexion]]></media:text>
                                <media:title type="plain"><![CDATA[a photo of the airpods max 2 and the sony 1000x the collexion]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/EsKsqEVsFK2YbjcXauk29T-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Sony's had something up its sleeve for weeks, and that something is finally here. Ever since <a href="https://www.reddit.com/r/headphones/comments/1t34bty/sony_1000xx_the_collexion_spotted_worn_by_damson/" target="_blank" rel="nofollow">paparazzi photos of an actor donning suspicious new headphones leaked</a>, the audio world's been holding its breath — but hold your breath no more. To celebrate the 10th anniversary of the XM line, the Japanese everything-brand is releasing its first uber-premium over-ear headphone: 1000X The ColleXion. </p><p>1000X The ColleXion is less aimed at the <a href="https://www.tomsguide.com/reviews/bose-quietcomfort-headphones">Bose QuietComfort</a> (not Ultra) crowd and the JBL Tour One M3 Pro crowd, and more at the <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review">AirPods Max 2</a>, <a href="https://www.tomsguide.com/audio/over-ear-headphones/i-just-tested-these-impeccable-hi-res-headphones-and-they-blow-away-the-airpods-max">Bowers and Wilkins Px8 S2</a> crowd. But how do these "premium" headphones actually compare to said competition? </p><p>To find out how 1000X The ColleXion <em>really</em> fares against the AirPods Max 2, I put the two flagship cans through a series of tests. Lock in, because it's about to move up a gear. Will the Sony 1000X The ColleXion or the AirPods Max 2 win? </p>        <div class="featured_product_block featured_block_versus" data-id="eefbffc2-718b-414b-91b5-04d40dd9db1c">            <a href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS6F226/" data-model-name="AirPods Max 2" 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/TSi2XMUrFhYn5nqrGBsd59.jpg" alt="the airpods max 2 on a white background showing all the color options"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                            <div class='featured__brand'>Apple</div>                    <div class="featured__title">AirPods Max 2</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>The Apple AirPods Max 2 are Apple's flagship over-ears, with seamless Apple ecosystem integration, premium sound, and classic Apple design. </p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_versus" data-id="a7fab935-a18e-4cd3-a8dd-05208599d50e">            <a href="https://www.amazon.com/Sony-COLLEXION-Cancelling-Headphones-Microphones/dp/B0GW9MBZJ9" data-model-name="1000X The Collexion" 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/MsaMjcX9k2kWR6V2YBTq9h.png" alt="the sony 1000x the collexion deal block image"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                            <div class='featured__brand'>Sony</div>                    <div class="featured__title">1000X The Collexion</div>                                    </div>                <div class="subtitle__description">                                                            <p><p>The 1000X The ColleXion are Sony's newest flagship headphones, combining premium materials and design with an even better sound profile. </p></p>                </div>                            </div>        </div><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-specs-compared"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Specs compared</span></h2><div ><table><tbody><tr><td class="firstcol empty" ></td><td  ><p><strong>Apple AirPods Max 2</strong></p></td><td  ><p><strong>Sony 1000X The Collexion</strong></p></td></tr><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS6F226/" target="_blank" rel="nofollow">$549</a> / <a href="https://www.amazon.co.uk/Apple-Headphones-Cancellation-Personalised-Translation/dp/B0GSS66JW8" target="_blank" rel="nofollow">£499</a></p></td><td  ><p><a href="https://www.amazon.com/Sony-COLLEXION-Cancelling-Headphones-Microphones/dp/B0GW9MBZJ9" target="_blank" rel="nofollow">$650 </a>/ <a href="https://www.amazon.co.uk/COLLEXION-Bluetooth-Headphones-Studio-Grade-Cancelling-Black/dp/B0GY4PPX7F" target="_blank" rel="nofollow">£549</a></p></td></tr><tr><td class="firstcol " ><p><strong>Headphone style</strong></p></td><td  ><p>Closed back</p></td><td  ><p>Closed back</p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>40mm dynamic</p></td><td  ><p>30mm dynamic</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz – 20KHz</p></td><td  ><p>20 Hz – 20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Impedance</strong></p></td><td  ><p>32Ω</p></td><td  ><p>24Ω (Passive) / 32Ω (Active)</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>No IP rating</p></td><td  ><p>No IP rating</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.3, USB-C wired</p></td><td  ><p>Bluetooth 6.0, LDAC, 3.5mm wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>Yes </p></td><td  ><p>Yes, up to 2 devices</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>Up to 20 hours (ANC on)</p></td><td  ><p>24 hours (ANC on)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>13.6 ounces</p></td><td  ><p>11.3 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Midnight, Starlight, Blue, Purple, Orange</p></td><td  ><p>Black, White</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-price"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Price</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="2aDfyW37Szj5dCTPo8QzPF" name="Sony-WH-1000-The-Collexion-7" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/2aDfyW37Szj5dCTPo8QzPF-1920-80.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 Sony 1000X The ColleXion are an eye-watering $659 at their May 2026 launch, while the AirPods Max 2 launched in April 2026 for $549. </p><p>You're paying $110 extra for The ColleXion — and the AirPods Max 2 were already outrageously expensive. I'm not too sure what this extra $100 is getting you — sure, The ColleXion look nice and are much more premium than the WH-1000XM6, but still, $650 is a bit of a stretch. </p><p><strong>Winner: </strong>AirPods Max 2 </p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-design"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: 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="qByoRqBT5yHC3DPHjNGi2F" name="AirpodsMax2_04.JPG" alt="the apple airpods max 2 in blue photographed against a blue tom's guide background, showing the mesh headband, anodized aluminum ear cups, carry case, and controls" src="https://cdn.mos.cms.futurecdn.net/qByoRqBT5yHC3DPHjNGi2F-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Right, so the AirPods Max 2 and the 1000X The ColleXion both serve a similar (unfortunate) purpose: being a fashion statement. Headphones <em>should</em> have one job — playing music — but Apple and Sony seem to be trying to make over-ears something you wear for the sole purpose of wearing them. </p><p>They have both kind of... succeeded, though? The AirPods Max 2 and ColleXion both look gorgeous, with aluminum and stainless steel finishes that scream premium. I suppose, if you're paying $550-$650 for headphones, you'd expect them to look good. Personally, I still think the AirPods Max 2 look better, but I know beauty is in the eye of the beholder, so it's fine if you disagree there.</p><p>The AirPods Max 2 have much more intuitive controls — no tapping randomly on the ear cups — but The ColleXion now have a Spatial Upmixer button, so you can cycle through music upmix and cinema upmix without the Sony Headphones app. I personally did not enjoy how the Spatial Upmixer made my music super tinny, so I never used this. </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="pdw8ak4ZDABxE6JqwA294Y" name="Sony-WH-1000-The-Collexion-2-LIST2" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/pdw8ak4ZDABxE6JqwA294Y-1920-80.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>However, while the AirPods Max 2 win in terms of design and controls, The ColleXion win in terms of carry case. The AirPods Max 2 have no case worth talking about, but The ColleXion have a pretty awesome hard shell mini handbag case. Not only does this protect the headphones, it also looks pretty stylish. As a result, I'd say this round is a draw.</p><p><strong>Winner: </strong>Draw. </p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-sound-quality"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Sound quality</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="n68Fe55iVgfGoUDwyXCN8F" name="AirpodsMax2_06.JPG" alt="the apple airpods max 2 in blue photographed against a blue tom's guide background, showing the mesh headband, anodized aluminum ear cups, carry case, and controls" src="https://cdn.mos.cms.futurecdn.net/n68Fe55iVgfGoUDwyXCN8F-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Both the 1000XX The ColleXion and the AirPods Max 2 sound divine. If you like expansive, melodic headphones capable of delicately balancing rich bass and subtle detail, you'll like both the Sony and Apple offerings. </p><p>To accurately compare the sound, I listened to both The ColleXion and the AirPods Max 2 as my daily drivers, playing every genre under the sun (including podcasts and YouTube videos). I listened to everything on Qobuz, the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for audiophiles.</p><p>'Neverender' by Justice & Tame Impala is an electronic track with an intense synth-heavy instrumental and infectious metallic percussion. Neither the ColleXion nor the AirPods Max 2 lost any detail, and the vocals had unbeatable clarity, even during heavier sections. </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="kgxmAATBMgwosMoz4rRorb" name="Sony-WH-1000-The-Collexion-3" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/kgxmAATBMgwosMoz4rRorb-1920-80.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>When I played a heavier rock track, 'RIDING WITH MY GIRLS' by Die Spitz, the ColleXion handled the crunchy bass and metal-tinged guitars well. I wanted a touch more depth to the percussion — the cymbals kind of faded into the background — but in general, the ColleXion performed well. </p><p>When I listened with the AirPods Max 2, the details were louder and pulled forward, including the cymbals and crunchy guitar effects. Neither headphones managed to make the bass sound particularly excellent, but the AirPods Max 2 created a livelier soundscape. </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="AXejktPxeoGE9bhCYDCfvE" name="AirpodsMax2_05.JPG" alt="the apple airpods max 2 in blue photographed against a blue tom's guide background, showing the mesh headband, anodized aluminum ear cups, carry case, and controls" src="https://cdn.mos.cms.futurecdn.net/AXejktPxeoGE9bhCYDCfvE-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Of course, I also had to test with LDAC, Sony's proprietary hi-res Bluetooth codec. 'Parachute' by Hayley Williams is a voice-led guitar track with heavy, almost over-processed bass and emotive vocals. Williams' voice sounded particularly incredible with LDAC enabled. </p><p>With the AirPods Max 2, 'Parachute' sounded clear, detailed, and had vibrant bass. Williams' voice was, again, front and center, weaving confidently between the over-processed bass and percussion. </p><p>The AirPods Max 2 <em>just</em> beat The ColleXion here — the ColleXion handle higher frequencies really well, but the bass was a bit too unrefined. </p><p>Both the AirPods Max 2 and the Sony 1000X The ColleXion have wired playback — USB-C and 3.5mm aux, respectively — which provides similarly lossless performances. Naturally, the AirPods Max 2 sound best via Apple Music and USB-C, and the Sonys sound best via Qobuz and 3.5mm wired. </p><p>Overall, both headphones sound great, and I'm sure you wouldn't be disappointed with either. The AirPods <em>just</em> take this round due to the more balanced bass response, though, as The ColleXion's bass can verge on unrefined in heavier genres. </p><p><strong>Winner: </strong>Apple<strong> </strong>AirPods Max 2</p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-anc"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: ANC</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="5qQPjACo9Hn4vHeQ7hedik" name="Sony-WH-1000-The-Collexion-6" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/5qQPjACo9Hn4vHeQ7hedik-1920-80.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>Both the AirPods Max 2 and The ColleXion have fantastic ANC, but the Sonys are the better performers here. During tests, I couldn't hear <em>anything</em> through the The ColleXion, not even high-frequency noises that often leak through even the strongest ANC, but I could hear things like clacking keyboards and chest-vibrating construction noises when listening to the AirPods Max 2. </p><p>For everyday use, though, you would get on just fine with either. I predominantly use my AirPods Max 2 and the ANC is everything I could need. </p><p><strong>Winner: </strong>Sony 1000X The ColleXion</p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-call-quality"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Call quality</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="YRAfCA4JGrzcNbkv4dXjTL" name="Sony-WH-1000-The-Collexion-14 (2)" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/YRAfCA4JGrzcNbkv4dXjTL-1920-80.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've said it before (and I'm sure I'll say it again), but we've reached a point in the Bluetooth headphone life cycle where pretty much every single flagship over-ears will provide you crystal-clear sound. </p><p>I have only good things to say about the AirPods Max 2 and Sony 1000X The ColleXion's call quality. Both headphones have crystal-clear audio, no fuzziness or hissing in sibilant words. Both headphones reduce ambient noise wonderfully, so I have no reservations about recommending either headphones for calls in loud or quiet environments. </p><p><strong>Winner: </strong>Draw. </p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-battery"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Battery</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="kcaAzw9zQYHJnoRKA7d5DS" name="Sony-WH-1000-The-Collexion-1-LIST" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/kcaAzw9zQYHJnoRKA7d5DS-1920-80.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 battery life is my biggest qualm with the AirPods Max 2. I can't quite believe Apple released headphones with a 20-hour battery life in 2026, but here we are. While The ColleXion have a 24-hour battery, this still isn't particularly impressive, either. </p><p>It's better, don't get me wrong — I'll take everything I can get as long as it's better than the AirPods Max 2 — but when the Sony WH-1000XM6 have a 30-hour battery life for $200 cheaper, this is a huge miss from Sony. </p><p>Of course, the Sonys win by default, but really, there are no winners here. </p><p><strong>Winner: </strong>Sony 1000X The ColleXion</p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-verdict"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: 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="SFZsRLkZwDr2CuvgqXtCEF" name="AirpodsMax2_07.JPG" alt="the apple airpods max 2 in blue photographed against a blue tom's guide background, showing the mesh headband, anodized aluminum ear cups, carry case, and controls" src="https://cdn.mos.cms.futurecdn.net/SFZsRLkZwDr2CuvgqXtCEF-1920-80.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Both the AirPods Max 2 and the Sony 1000X The ColleXion received 85 points, making this versus a draw. You won't be disappointed by either the AirPods Max 2 or The ColleXion. </p><p>It's near impossible to choose between these two headphones. Both ooze style, have premium sound quality, and an array of impressive features (although I disliked Sony's Spatial Upmixer, you might love it). Both earn their place as some of the most premium, <a href="https://www.tomsguide.com/us/best-wireless-headphones,review-5565.html">best wireless headphones</a> money can buy. </p><p>I would personally choose the Apple AirPods Max 2, but The ColleXion still have a lot going for them: Want lighter cans, don't mind a more unrefined bass, and love Sony's impeccable ANC? Go for the 1000X The ColleXion. If you value metal construction, vivacious sound, and don't mind a poorer battery life, the AirPods will treat you right. </p><h2 class="article-body__section" id="section-sony-1000x-the-collexion-vs-apple-airpods-max-2-ratings-scorecard"><span>Sony 1000X The ColleXion vs Apple AirPods Max 2: Ratings scorecard</span></h2><div ><table><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>AirPods Max 2</strong></p></th><th  ><p><strong>Sony 1000XX The ColleXion</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Price (5)</strong></p></td><td  ><p>3</p></td><td  ><p>2</p></td></tr><tr><td class="firstcol " ><p><strong>Design & controls (15)</strong></p></td><td  ><p>13</p></td><td  ><p>13</p></td></tr><tr><td class="firstcol " ><p><strong>Features (10)</strong></p></td><td  ><p>10</p></td><td  ><p>9</p></td></tr><tr><td class="firstcol " ><p><strong>Sound quality (25)</strong></p></td><td  ><p>25</p></td><td  ><p>24</p></td></tr><tr><td class="firstcol " ><p><strong>ANC (20)</strong></p></td><td  ><p>17</p></td><td  ><p>18</p></td></tr><tr><td class="firstcol " ><p><strong>Call quality (10)</strong></p></td><td  ><p>9</p></td><td  ><p>9</p></td></tr><tr><td class="firstcol " ><p><strong>Battery (15)</strong></p></td><td  ><p>8</p></td><td  ><p>10</p></td></tr><tr><td class="firstcol " ><p><strong>Total score (100)</strong></p></td><td  ><p>85</p></td><td  ><p>85</p></td></tr></tbody></table></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Sony just launched the new 1000X The ColleXion headphones — here's everything you need to know about this premium audio upgrade ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Premium isn't new territory for Sony. The impressive build quality has made the 1000X series stand out from the crowd, becoming a top choice for listeners in the audio world. Today, the company announced a jump into the world of luxury audio with The 1000X ColleXion headphones. </p><p>The new headphones are Sony's take on refined luxury, with an uber sleek look that's totally eye-catching. The headphones were first leaked when Damson Idris, star of "F1: The Movie" was spotted wearing them out in public, and speculations have been swirling about the headphones' capabilities ever since. </p><p>They still have the distinct Sony headphone look and take inspiration from the <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review">WH-1000XM6</a> headphones, but it's clear that the quality has been upped here. But at what cost? Well, it's a big one, so hold onto your hats as we get into everything you need to know about the 1000X The ColleXion headphones launch. </p><h2 id="how-much-do-they-cost">How much do they cost?</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="kgxmAATBMgwosMoz4rRorb" name="Sony-WH-1000-The-Collexion-3" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/kgxmAATBMgwosMoz4rRorb-1920-80.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 you can imagine, since these are luxury headphones, they come with a luxury price point. The new 1000X The Collection headphones are available to purchase for <a href="https://www.sony.co.uk/headphones/products/1000x-the-collexion">$649</a> /<a href="https://www.sony.co.uk/headphones/products/1000x-the-collexion">£550</a> / <a href="https://www.sony.com.au/headphones/products/1000x-the-collexion" target="_blank">AU$999</a> from the <a href="https://www.sony.co.uk/headphones/products/1000x-the-collexion">Sony Online store</a>.</p><p>In the U.S. and U.K., they're even pricier than the Apple <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review">AirPods Max 2</a>, which cost $549 / £499, though in Australia, they're priced identically at AU$999. </p><p>Since Sony is a premium audio brand, their regular gear isn't exactly budget; its previous release, the <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review">WH-1000XM6</a> headphones, cost $399 / £379 / AU$649. </p><h2 id="what-do-they-look-like">What do they look like?</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="LsPgaZpsWL76awiAHwtS39" name="Sony-WH-1000-The-Collexion-14 (1)" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/LsPgaZpsWL76awiAHwtS39-1920-80.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 design of the new 1000X The ColleXion headphones clearly takes heavy inspiration from the <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review">WH-1000XM6</a> headphones, which were released last year. But there are some minor changes, which primarily include the materials used. </p><p>First, the headphones are available in Platinum or Black. This is a very basic color range, but we have previously seen Sony release more colors of headphones at a later date, so could it be something we see here? For example, the company is also launching a new Sandstone colorway for the WH-1000XM6 headphones today. </p><p>The headband of the new ColleXion headphones is metal with padding underneath. The earcups have also had a design change and are now covered in faux leather, but keep the same distinctive Sony shape. However, the earcup of the new headphones is a slimmer 40.1mm, compared to the XM6 earcups, which are 45.4mm. </p><h2 id="audio-upgrades">Audio upgrades</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="CCPLm5v3Tzxf6kGZgkQWRg" name="Sony-WH-1000-The-Collexion-12 (1)" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/CCPLm5v3Tzxf6kGZgkQWRg-1920-80.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 1000X The ColleXion headphones see the introduction of DSEE Ultimate. Previously, only standard DSEE has been available in Sony's audio gear, and this technology rebuilds compressed music files. The Ultimate version uses AI technology to upscale the sampling rate (resolution) and bit depth (dynamic range), restoring any lost detail. </p><p>The ColleXion headphones also have Hi-Res wireless and LDAC audio codecs. The sound has also been optimized with Walkman technology, using more advanced internal components. </p><p>ColleXion has three modes of 360 Upmix for stereo music, cinema and gaming. This aims to give a more immersive experience across the listening modes. </p><h2 id="general-performance">General performance</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="2aDfyW37Szj5dCTPo8QzPF" name="Sony-WH-1000-The-Collexion-7" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/2aDfyW37Szj5dCTPo8QzPF-1920-80.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>1000X The ColleXion features 12 microphones (six on each earcup) and an Adaptive NC Optimizer seen in the WH-1000XM6 for noise cancelling that adapts to the environment around you. They also allow background music options with the Sound Connect app, which is one of my favorite features to use on the <a href="https://www.tomsguide.com/audio/earbuds/sony-wf-1000xm6-review">WF-1000XM6</a> earbuds. </p><p>With 24 hours of battery life with noise cancelling turned on and 32 hours with it turned off, the ColleXion headphones can support all-day listening. This is a significant reduction from what we see in the XM6 headphones, which support 30 hours with ANC and 40 hours with ANC turned off. But with that said, it's still more than enough to get you through the longest flights. </p><p>What do you think? Is it worth the upgrade to the premium model, or will you be sticking with the 1000X release from last year? Let us know in the comments. </p><p>Don't forget to check out our full Sony 1000X ColleXion review. </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/audio/earbuds/sony-wf-1000xm6-review"><strong>I tested the highly anticipated Sony WF-1000XM6 — and they're 'everything I could possibly need'</strong></a></li><li><a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review"><strong>Apple AirPods Max 2 review: ‘Everything from its predecessor dialed up to 11’</strong></a></li><li><a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review"><strong>I tested the Sony WH-1000XM6 for three weeks — here’s my verdict</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/headphones/sony-just-launched-the-new-1000x-the-collexion-headphones-heres-everything-you-need-to-know-about-this-premium-audio-upgrade</link>
                                                                            <description>
                            <![CDATA[ Sony announces the launch of the new luxury 1000X The ColleXion headphones — this is everything you need to know, including the price, design and audio specs. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">bkiyaS5p2LpQkr4QvhxLaY</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/pdw8ak4ZDABxE6JqwA294Y-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 19 May 2026 16:00:00 +0000</pubDate>                                                                                                                                <updated>Wed, 20 May 2026 01:55:25 +0000</updated>
                                                                                                                                            <category><![CDATA[Headphones]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                                                                <author><![CDATA[ ashley.thieme@futurenet.com (Ashley Thieme) ]]></author>                    <dc:creator><![CDATA[ Ashley Thieme ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3AWovHjApwuNrSGRS6WBcL-320-70.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ashley Thieme is a staff writer on the Reviews team at Tom’s Guide where she tests out the latest tech so you can know what’s going to be worth your time and money. She has a master’s degree in Magazine Journalism and a bachelor’s degree in Journalism, Media and Sociology from Cardiff University. She has bylines in titles including Women’s Health UK, sharing the latest health and wellness news stories, and Virgin Radio UK, providing the latest entertainment news and working on celebrity interviews. She has experience reporting on a variety of topics including music, literature, motorsport, entertainment and health. In previously published work, she has reviewed live music events, books, and wellness products. She values the importance of tech enhancing your life rather than taking over, and as a music fanatic, she is always looking for the best way to listen to new music releases. Discovering the top audio equipment that enhances sound quality and provides optimum comfort is what Ashley does best. In her spare time, Ashley enjoys hitting her reading goals on Goodreads by getting into the latest novels with a cup of tea as well as getting out in the Welsh mountains for a good hike on the weekend.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/pdw8ak4ZDABxE6JqwA294Y-1920-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sony WH-1000X The Collexion]]></media:description>                                                            <media:text><![CDATA[Sony WH-1000X The Collexion]]></media:text>
                                <media:title type="plain"><![CDATA[Sony WH-1000X The Collexion]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/pdw8ak4ZDABxE6JqwA294Y-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Premium isn't new territory for Sony. The impressive build quality has made the 1000X series stand out from the crowd, becoming a top choice for listeners in the audio world. Today, the company announced a jump into the world of luxury audio with The 1000X ColleXion headphones. </p><p>The new headphones are Sony's take on refined luxury, with an uber sleek look that's totally eye-catching. The headphones were first leaked when Damson Idris, star of "F1: The Movie" was spotted wearing them out in public, and speculations have been swirling about the headphones' capabilities ever since. </p><p>They still have the distinct Sony headphone look and take inspiration from the <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review">WH-1000XM6</a> headphones, but it's clear that the quality has been upped here. But at what cost? Well, it's a big one, so hold onto your hats as we get into everything you need to know about the 1000X The ColleXion headphones launch. </p><h2 id="how-much-do-they-cost">How much do they cost?</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="kgxmAATBMgwosMoz4rRorb" name="Sony-WH-1000-The-Collexion-3" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/kgxmAATBMgwosMoz4rRorb-1920-80.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 you can imagine, since these are luxury headphones, they come with a luxury price point. The new 1000X The Collection headphones are available to purchase for <a href="https://www.sony.co.uk/headphones/products/1000x-the-collexion">$649</a> /<a href="https://www.sony.co.uk/headphones/products/1000x-the-collexion">£550</a> / <a href="https://www.sony.com.au/headphones/products/1000x-the-collexion" target="_blank">AU$999</a> from the <a href="https://www.sony.co.uk/headphones/products/1000x-the-collexion">Sony Online store</a>.</p><p>In the U.S. and U.K., they're even pricier than the Apple <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review">AirPods Max 2</a>, which cost $549 / £499, though in Australia, they're priced identically at AU$999. </p><p>Since Sony is a premium audio brand, their regular gear isn't exactly budget; its previous release, the <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review">WH-1000XM6</a> headphones, cost $399 / £379 / AU$649. </p><h2 id="what-do-they-look-like">What do they look like?</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="LsPgaZpsWL76awiAHwtS39" name="Sony-WH-1000-The-Collexion-14 (1)" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/LsPgaZpsWL76awiAHwtS39-1920-80.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 design of the new 1000X The ColleXion headphones clearly takes heavy inspiration from the <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review">WH-1000XM6</a> headphones, which were released last year. But there are some minor changes, which primarily include the materials used. </p><p>First, the headphones are available in Platinum or Black. This is a very basic color range, but we have previously seen Sony release more colors of headphones at a later date, so could it be something we see here? For example, the company is also launching a new Sandstone colorway for the WH-1000XM6 headphones today. </p><p>The headband of the new ColleXion headphones is metal with padding underneath. The earcups have also had a design change and are now covered in faux leather, but keep the same distinctive Sony shape. However, the earcup of the new headphones is a slimmer 40.1mm, compared to the XM6 earcups, which are 45.4mm. </p><h2 id="audio-upgrades">Audio upgrades</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="CCPLm5v3Tzxf6kGZgkQWRg" name="Sony-WH-1000-The-Collexion-12 (1)" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/CCPLm5v3Tzxf6kGZgkQWRg-1920-80.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 1000X The ColleXion headphones see the introduction of DSEE Ultimate. Previously, only standard DSEE has been available in Sony's audio gear, and this technology rebuilds compressed music files. The Ultimate version uses AI technology to upscale the sampling rate (resolution) and bit depth (dynamic range), restoring any lost detail. </p><p>The ColleXion headphones also have Hi-Res wireless and LDAC audio codecs. The sound has also been optimized with Walkman technology, using more advanced internal components. </p><p>ColleXion has three modes of 360 Upmix for stereo music, cinema and gaming. This aims to give a more immersive experience across the listening modes. </p><h2 id="general-performance">General performance</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="2aDfyW37Szj5dCTPo8QzPF" name="Sony-WH-1000-The-Collexion-7" alt="Sony WH-1000X The Collexion" src="https://cdn.mos.cms.futurecdn.net/2aDfyW37Szj5dCTPo8QzPF-1920-80.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>1000X The ColleXion features 12 microphones (six on each earcup) and an Adaptive NC Optimizer seen in the WH-1000XM6 for noise cancelling that adapts to the environment around you. They also allow background music options with the Sound Connect app, which is one of my favorite features to use on the <a href="https://www.tomsguide.com/audio/earbuds/sony-wf-1000xm6-review">WF-1000XM6</a> earbuds. </p><p>With 24 hours of battery life with noise cancelling turned on and 32 hours with it turned off, the ColleXion headphones can support all-day listening. This is a significant reduction from what we see in the XM6 headphones, which support 30 hours with ANC and 40 hours with ANC turned off. But with that said, it's still more than enough to get you through the longest flights. </p><p>What do you think? Is it worth the upgrade to the premium model, or will you be sticking with the 1000X release from last year? Let us know in the comments. </p><p>Don't forget to check out our full Sony 1000X ColleXion review. </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/audio/earbuds/sony-wf-1000xm6-review"><strong>I tested the highly anticipated Sony WF-1000XM6 — and they're 'everything I could possibly need'</strong></a></li><li><a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review"><strong>Apple AirPods Max 2 review: ‘Everything from its predecessor dialed up to 11’</strong></a></li><li><a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review"><strong>I tested the Sony WH-1000XM6 for three weeks — here’s my verdict</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The Canon EOS R6 V and the Sony A7R VI dropped this week — both look incredible but here’s the one I’m most excited for (and why) ]]></title>
                                                                                                <dc:content><![CDATA[ <p>It's been quite an eventful few week for cameras. Insta360 launched a <a href="https://www.tomsguide.com/cameras-photography/groovy-baby-insta360-launches-latest-go-3s-bundle-inspired-by-retro-film-photography">Go 3S Retro Bundle</a>, we saw the <a href="https://www.tomsguide.com/cameras-photography/i-saw-the-gopro-mission-1-and-this-new-action-camera-could-make-all-point-and-shoots-obsolete-heres-why">GoPro Mission 1 lineup</a>, and on Wednesday May 13th, Sony and Canon dropped a camera each — what a time to be alive! Sony and Canon's  announcements came out of the blue, with teasers posted on their respective social channels either a week or just a few days prior to the launches.</p><p>Canon has expanded its V-series lineup of video-first cameras with the new EOS R6 V, while the Sony A7R VI succeeds the four-years-old <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a7r-v-review">A7R V</a>. Sony's offering sounds like the ultimate hybrid that builds on its predecessor's success, while Canon's latest camera looks like it could be the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless camera</a> for video.</p><p>Needless to say, I'm excited and thrilled. As someone who reviews cameras for a living and loves photographing her surroundings, I'm intrigued by both cameras. But is there one I'm excited about more than the other? Well, that's what we're here to find out.</p><h2 id="a-quick-rundown-of-specs">A quick rundown of specs</h2><div ><table><thead><tr><th class="firstcol " ><p><strong>Specs</strong></p></th><th  ><p><strong>Sony A7R VI</strong></p></th><th  ><p>Canon EOS R6 V</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow">$4,498</a> / <a href="https://www.lcegroup.co.uk/New/Sony-A7R-VI-Mirrorless-Camera-Body_229893.html" target="_blank" rel="nofollow">£4,399</a></p></td><td  ><p><a href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow">$2,499</a> / <a href="https://www.lcegroup.co.uk/New/Canon-EOS-R6-V-Mirrorless-Camera-Body_229883.html" target="_blank" rel="nofollow">£2,399</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>66.8MP Stacked Exmor RS BSI full frame</p></td><td  ><p>32.5MP CMOS full frame</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>BIONZ XR2</p></td><td  ><p>DIGIC X</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Up to 8.5-stops IBIS</p></td><td  ><p>Up to 8.5-stops IBIS</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Real-time Recognition AF+</p></td><td  ><p>Dual Pixel CMOS  II</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>0.64-type Quad-XGA OLED, 9.44M dots, 120fps refresh rate</p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>3.2-inch 4-axis multi-angle LCD, 2.1M dots</p></td><td  ><p>3-inch vari-angle, 1.62M dots</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>8K/30fps and 4K/120fps</p></td><td  ><p>7K/60fps RAW and 7K/30fps Open Gate</p></td></tr><tr><td class="firstcol " ><p><strong>Max shooting speed</strong></p></td><td  ><p>30fps</p></td><td  ><p>40fps</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life (Stills, CIPA)</strong></p></td><td  ><p>600 shots (viewfinder)</p></td><td  ><p>640 shots</p></td></tr></tbody></table></div><h2 id="who-are-the-canon-eos-r6-v-and-sony-a7r-vi-for">Who are the Canon EOS R6 V and Sony A7R VI for?</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:1813px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="7mpPzNdW9KgsGjShTnvAH4" name="Canon-EOSR6V-1" alt="Canon EOS R6 V" src="https://cdn.mos.cms.futurecdn.net/7mpPzNdW9KgsGjShTnvAH4-1920-80.jpg" mos="" align="middle" fullscreen="" width="1813" height="1020" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Canon)</span></figcaption></figure><p>Both the Canon EOS R6 V and the Sony A7R VI have different target audiences — but with some overlap. The A7R VI sets a new gold standard for high-resolution full frame cameras with its 66.8MP sensor and 30fps RAW burst capabilities, which make it great for all genres of photography, including wildlife, sports, and portrait. It also features 8K/30fps video so it sounds like a fantastic all-rounder.</p><p>Bolstering the A7R VI's position as a powerful hybrid camera is its OLED viewfinder with a 120fps refresh rate and 9.44M-dot resolution. You also get a 3.2-inch 4-axis LCD monitor with a 2.1M-dot resolution. Oh, and 8.5-stops of in-body image stabilization (IBIS) which means you can shoot at very slow shutter speeds and still walk away with a usable photo.</p><div class="product"><a data-dimension112="dc23baf3-0c00-47fe-b096-6a7fa2c51761" data-action="Deal Block" data-label="A7R VI" data-dimension48="A7R VI" data-dimension25="$4498" href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" 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="WZxeQLkZhoTgmrzRbKRu5L" name="A7RVI-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WZxeQLkZhoTgmrzRbKRu5L-1920-80.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony A7R VI is a professional-grade hybrid camera that utilizes a newly developed 66.8MP sensor with 30fps RAW burst shooting and can shoot 8K/30fps video. It also offers up to 8.5-stops of IBIS, and packs a high-res EVF as well as a 4-axis LCD monitor.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow" data-dimension112="dc23baf3-0c00-47fe-b096-6a7fa2c51761" data-action="Deal Block" data-label="A7R VI" data-dimension48="A7R VI" data-dimension25="$4498">View Deal</a></p></div><p>And what about the EOS R6 V? It's the second R-V body Canon has introduced, and it joins the excellent <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r50-v-review">Canon EOS R50 V</a>. The 'V' version of the EOS R6 is also the <em>fourth </em>EOS R6 body in existence — and as the 'V' suggests, it's a video-first take on Canon's hybrid camera. There's no viewfinder so you must rely on the camera's 3-inch vari-angle screen and its 1.62M-dot resolution. The EOS R6 V offers 7K/60fps RAW and 7K/30fps Open Gate video capabilities, both of which make it ideal for content creation.</p><div class="product"><a data-dimension112="017f9ea4-9c85-4f62-a759-15c236ba35d9" data-action="Deal Block" data-label="EOS R6 V" data-dimension48="EOS R6 V" data-dimension25="$2499" href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" 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="p5QzTEnfDQHjKyd6QWp2E4" name="EOSR6V-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/p5QzTEnfDQHjKyd6QWp2E4-1920-80.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Canon EOS R6 V is a compact video-first full frame camera for content creators, and it features a 32.5MP sensor, 7K/60fps RAW and 7K/30fps Open Gate capabilities. It utilizes Canon's excellent Dual Pixel CMOS II autofocus system and up to 8.5-stops of IBIS too.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow" data-dimension112="017f9ea4-9c85-4f62-a759-15c236ba35d9" data-action="Deal Block" data-label="EOS R6 V" data-dimension48="EOS R6 V" data-dimension25="$2499">View Deal</a></p></div><p>In addition to that, you get 40fps continuous shooting which is... surprising, and something I wasn't really expecting in a video-first camera. While this camera sounds like it would be peerless for video, I'm sure it will be ideal for capturing photos of birds in flight too with its 32.5MP CMOS sensor and best-in-class Dual Pixel CMOS II autofocus system. And did I mention it packs up to 8.5-stops of IBIS, just like the A7R VI? Yep, it's almost got it all.</p><h2 id="the-one-i-m-most-excited-for-and-why">The one I'm most excited for (and why)</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:800px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9kA4YPjZVRCKMM4vA8LnF4" name="Canon-EOSR6V-2" alt="Canon EOS R6 V" src="https://cdn.mos.cms.futurecdn.net/9kA4YPjZVRCKMM4vA8LnF4-1920-80.jpg" mos="" align="middle" fullscreen="" width="800" height="450" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Canon)</span></figcaption></figure><p>I'd be lying if I said I wasn't intrigued by the Canon EOS R6 V. It features the same 32.5MP sensor as the Canon EOS R6 Mark III. As a video-first iteration of the series, it packs an active cooling fan — which you'll most certainly need if you're shooting 7K/30fps Open Gate... or even 4K/60fps, really, especially outdoors.</p><p>With the EOS R6 Mark III and its predecessors, battery life while recording video was always a sticking point, but this time around, Canon's going for the "best battery life" crown. The company claims the EOS R6 V can record for two hours or more as long as the built-in fan is toggled on, the overheat limit is set to high, and the camera's on a tripod.</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:5831px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4Bq6cCVFc3T9jxaswjWAGL" name="Sony-A7RVI-1" alt="Sony A7R VI" src="https://cdn.mos.cms.futurecdn.net/4Bq6cCVFc3T9jxaswjWAGL-1920-80.jpg" mos="" align="middle" fullscreen="" width="5831" height="3280" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>While all of this sounds great (and I'm looking forward to test the camera for my full review), the EOS R6 V isn't the one I'm most excited about — it's the Sony A7R VI. The A7R VI packs a newly developed 66.8MP sensor and enables you to shoot RAW photos at 30fps. This will certainly be a boon for wildlife photographers such as myself.</p><p>I loved the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">Sony A1 II</a> when I tested it and it's the one camera I've thought about buying myself time and again. I captured some of my best photos on it, and being able to make extreme crops was a walk in the park thanks to the 50MP resolution. But 66.8MP sounds even better, and the sensor's stacked nature delivers faster readout speeds and minimizes rolling shutter distortion. Then you've got the new BIONZ XR2 processor for 30fps blackout-free bursts, so you don't lose track of your subject because of, well, black screens between shots.</p><p>The A7R VI also borrows Auto subject detection mode from the A1 II, so you don't need to manually change the subject you're photographing — handy for anyone who captures a variety of subject. I loved this on the A1 II as I photograph birds, animals and (occasionally) humans and I can't wait to see how it holds up on the A7R VI.</p><h2 id="a-new-trick-up-its-sleeve">A new trick up its sleeve</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:1645px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="P5su7FzHCBigT75dh2KceZ" name="Sony-A7RVI-2" alt="Sony A7R VI" src="https://cdn.mos.cms.futurecdn.net/P5su7FzHCBigT75dh2KceZ-1920-80.jpg" mos="" align="middle" fullscreen="" width="1645" height="926" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>In addition to all the goodies the Sony A7R VI offers, it also features a new battery pack: the 2,670mAh NP-SA100. Sony says that with this battery pack, the A7R VI is CIPA-rated for 600 shots with the viewfinder or 710 shots with the monitor. Its predecessor, the A7R V, is CIPA-rated for 440 shots, so Sony's made some strides.</p><p>CIPA testing is done under strict conditions and the number is often conservative, so it'll be interesting to see how the battery holds up in real-world use.</p><p>I think it's going to be interesting to see how both the A7R VI and the Canon EOS R6 V perform in practice as, in theory, they both sound great in their own right. I'm obviously not pitting both cameras against one another. That would be unfair as they have different target audiences (and there's quite a bit of a price difference). Needless to say, though, it's an exciting time for photographers and camera enthusiasts. I can't wait to get my hands on both cameras and put them through their paces!</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eJkPyW"></div>                            </div>                            <script src="https://kwizly.com/embed/eJkPyW.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/cameras-photography/mirrorless-cameras/ive-got-two-masters-degrees-and-the-sony-a1-ii-makes-me-feel-stupid-heres-why">I tested the Sony A1 II for 2 weeks — and its autofocus results blew my mind</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-just-tested-the-canon-eos-r5-mark-ii-to-photograph-wildlife-and-this-one-feature-has-changed-how-i-take-photos-forever">I just tested the Canon EOS R5 Mark II to photograph wildlife — and this one feature has changed how I take photos forever</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-wildlife-camera-first-impressions">‘Few wildlife cameras come close to this’: why the OM System OM-1 Mark II is perfect for photographing birds in flight and other animals</a></li></ul><div data-widget-type="multimodelreview" data-model-name="Sony A7R VI,Canon EOS R6 V"></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/mirrorless-cameras/the-canon-eos-r6-v-and-the-sony-a7r-vi-dropped-this-week-both-look-incredible-but-heres-the-one-im-most-excited-for-and-why</link>
                                                                            <description>
                            <![CDATA[ The Canon EOS R6 V and the Sony A7R VI are here, and both cameras sound powerful and offer best-in-class features. But there's one camera I'm more excited for. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">UAJpDjwJcWMv6KVG2RhuF6</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/RNWmMtFfENFb7ADz2uZgbG-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 17 May 2026 10:45:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Mirrorless Cameras]]></category>
                                                    <category><![CDATA[Cameras & Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3-320-70.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&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/RNWmMtFfENFb7ADz2uZgbG-1920-80.jpg">
                                                            <media:credit><![CDATA[Canon / Sony]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Canon EOS R6 V and Sony A7R VI]]></media:description>                                                            <media:text><![CDATA[Canon EOS R6 V and Sony A7R VI]]></media:text>
                                <media:title type="plain"><![CDATA[Canon EOS R6 V and Sony A7R VI]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/RNWmMtFfENFb7ADz2uZgbG-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It's been quite an eventful few week for cameras. Insta360 launched a <a href="https://www.tomsguide.com/cameras-photography/groovy-baby-insta360-launches-latest-go-3s-bundle-inspired-by-retro-film-photography">Go 3S Retro Bundle</a>, we saw the <a href="https://www.tomsguide.com/cameras-photography/i-saw-the-gopro-mission-1-and-this-new-action-camera-could-make-all-point-and-shoots-obsolete-heres-why">GoPro Mission 1 lineup</a>, and on Wednesday May 13th, Sony and Canon dropped a camera each — what a time to be alive! Sony and Canon's  announcements came out of the blue, with teasers posted on their respective social channels either a week or just a few days prior to the launches.</p><p>Canon has expanded its V-series lineup of video-first cameras with the new EOS R6 V, while the Sony A7R VI succeeds the four-years-old <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a7r-v-review">A7R V</a>. Sony's offering sounds like the ultimate hybrid that builds on its predecessor's success, while Canon's latest camera looks like it could be the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless camera</a> for video.</p><p>Needless to say, I'm excited and thrilled. As someone who reviews cameras for a living and loves photographing her surroundings, I'm intrigued by both cameras. But is there one I'm excited about more than the other? Well, that's what we're here to find out.</p><h2 id="a-quick-rundown-of-specs">A quick rundown of specs</h2><div ><table><thead><tr><th class="firstcol " ><p><strong>Specs</strong></p></th><th  ><p><strong>Sony A7R VI</strong></p></th><th  ><p>Canon EOS R6 V</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow">$4,498</a> / <a href="https://www.lcegroup.co.uk/New/Sony-A7R-VI-Mirrorless-Camera-Body_229893.html" target="_blank" rel="nofollow">£4,399</a></p></td><td  ><p><a href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow">$2,499</a> / <a href="https://www.lcegroup.co.uk/New/Canon-EOS-R6-V-Mirrorless-Camera-Body_229883.html" target="_blank" rel="nofollow">£2,399</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>66.8MP Stacked Exmor RS BSI full frame</p></td><td  ><p>32.5MP CMOS full frame</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>BIONZ XR2</p></td><td  ><p>DIGIC X</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Up to 8.5-stops IBIS</p></td><td  ><p>Up to 8.5-stops IBIS</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Real-time Recognition AF+</p></td><td  ><p>Dual Pixel CMOS  II</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>0.64-type Quad-XGA OLED, 9.44M dots, 120fps refresh rate</p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>3.2-inch 4-axis multi-angle LCD, 2.1M dots</p></td><td  ><p>3-inch vari-angle, 1.62M dots</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>8K/30fps and 4K/120fps</p></td><td  ><p>7K/60fps RAW and 7K/30fps Open Gate</p></td></tr><tr><td class="firstcol " ><p><strong>Max shooting speed</strong></p></td><td  ><p>30fps</p></td><td  ><p>40fps</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life (Stills, CIPA)</strong></p></td><td  ><p>600 shots (viewfinder)</p></td><td  ><p>640 shots</p></td></tr></tbody></table></div><h2 id="who-are-the-canon-eos-r6-v-and-sony-a7r-vi-for">Who are the Canon EOS R6 V and Sony A7R VI for?</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:1813px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="7mpPzNdW9KgsGjShTnvAH4" name="Canon-EOSR6V-1" alt="Canon EOS R6 V" src="https://cdn.mos.cms.futurecdn.net/7mpPzNdW9KgsGjShTnvAH4-1920-80.jpg" mos="" align="middle" fullscreen="" width="1813" height="1020" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Canon)</span></figcaption></figure><p>Both the Canon EOS R6 V and the Sony A7R VI have different target audiences — but with some overlap. The A7R VI sets a new gold standard for high-resolution full frame cameras with its 66.8MP sensor and 30fps RAW burst capabilities, which make it great for all genres of photography, including wildlife, sports, and portrait. It also features 8K/30fps video so it sounds like a fantastic all-rounder.</p><p>Bolstering the A7R VI's position as a powerful hybrid camera is its OLED viewfinder with a 120fps refresh rate and 9.44M-dot resolution. You also get a 3.2-inch 4-axis LCD monitor with a 2.1M-dot resolution. Oh, and 8.5-stops of in-body image stabilization (IBIS) which means you can shoot at very slow shutter speeds and still walk away with a usable photo.</p><div class="product"><a data-dimension112="dc23baf3-0c00-47fe-b096-6a7fa2c51761" data-action="Deal Block" data-label="A7R VI" data-dimension48="A7R VI" data-dimension25="$4498" href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" 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="WZxeQLkZhoTgmrzRbKRu5L" name="A7RVI-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WZxeQLkZhoTgmrzRbKRu5L-1920-80.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony A7R VI is a professional-grade hybrid camera that utilizes a newly developed 66.8MP sensor with 30fps RAW burst shooting and can shoot 8K/30fps video. It also offers up to 8.5-stops of IBIS, and packs a high-res EVF as well as a 4-axis LCD monitor.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow" data-dimension112="dc23baf3-0c00-47fe-b096-6a7fa2c51761" data-action="Deal Block" data-label="A7R VI" data-dimension48="A7R VI" data-dimension25="$4498">View Deal</a></p></div><p>And what about the EOS R6 V? It's the second R-V body Canon has introduced, and it joins the excellent <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r50-v-review">Canon EOS R50 V</a>. The 'V' version of the EOS R6 is also the <em>fourth </em>EOS R6 body in existence — and as the 'V' suggests, it's a video-first take on Canon's hybrid camera. There's no viewfinder so you must rely on the camera's 3-inch vari-angle screen and its 1.62M-dot resolution. The EOS R6 V offers 7K/60fps RAW and 7K/30fps Open Gate video capabilities, both of which make it ideal for content creation.</p><div class="product"><a data-dimension112="017f9ea4-9c85-4f62-a759-15c236ba35d9" data-action="Deal Block" data-label="EOS R6 V" data-dimension48="EOS R6 V" data-dimension25="$2499" href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" 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="p5QzTEnfDQHjKyd6QWp2E4" name="EOSR6V-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/p5QzTEnfDQHjKyd6QWp2E4-1920-80.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Canon EOS R6 V is a compact video-first full frame camera for content creators, and it features a 32.5MP sensor, 7K/60fps RAW and 7K/30fps Open Gate capabilities. It utilizes Canon's excellent Dual Pixel CMOS II autofocus system and up to 8.5-stops of IBIS too.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow" data-dimension112="017f9ea4-9c85-4f62-a759-15c236ba35d9" data-action="Deal Block" data-label="EOS R6 V" data-dimension48="EOS R6 V" data-dimension25="$2499">View Deal</a></p></div><p>In addition to that, you get 40fps continuous shooting which is... surprising, and something I wasn't really expecting in a video-first camera. While this camera sounds like it would be peerless for video, I'm sure it will be ideal for capturing photos of birds in flight too with its 32.5MP CMOS sensor and best-in-class Dual Pixel CMOS II autofocus system. And did I mention it packs up to 8.5-stops of IBIS, just like the A7R VI? Yep, it's almost got it all.</p><h2 id="the-one-i-m-most-excited-for-and-why">The one I'm most excited for (and why)</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:800px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9kA4YPjZVRCKMM4vA8LnF4" name="Canon-EOSR6V-2" alt="Canon EOS R6 V" src="https://cdn.mos.cms.futurecdn.net/9kA4YPjZVRCKMM4vA8LnF4-1920-80.jpg" mos="" align="middle" fullscreen="" width="800" height="450" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Canon)</span></figcaption></figure><p>I'd be lying if I said I wasn't intrigued by the Canon EOS R6 V. It features the same 32.5MP sensor as the Canon EOS R6 Mark III. As a video-first iteration of the series, it packs an active cooling fan — which you'll most certainly need if you're shooting 7K/30fps Open Gate... or even 4K/60fps, really, especially outdoors.</p><p>With the EOS R6 Mark III and its predecessors, battery life while recording video was always a sticking point, but this time around, Canon's going for the "best battery life" crown. The company claims the EOS R6 V can record for two hours or more as long as the built-in fan is toggled on, the overheat limit is set to high, and the camera's on a tripod.</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:5831px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4Bq6cCVFc3T9jxaswjWAGL" name="Sony-A7RVI-1" alt="Sony A7R VI" src="https://cdn.mos.cms.futurecdn.net/4Bq6cCVFc3T9jxaswjWAGL-1920-80.jpg" mos="" align="middle" fullscreen="" width="5831" height="3280" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>While all of this sounds great (and I'm looking forward to test the camera for my full review), the EOS R6 V isn't the one I'm most excited about — it's the Sony A7R VI. The A7R VI packs a newly developed 66.8MP sensor and enables you to shoot RAW photos at 30fps. This will certainly be a boon for wildlife photographers such as myself.</p><p>I loved the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">Sony A1 II</a> when I tested it and it's the one camera I've thought about buying myself time and again. I captured some of my best photos on it, and being able to make extreme crops was a walk in the park thanks to the 50MP resolution. But 66.8MP sounds even better, and the sensor's stacked nature delivers faster readout speeds and minimizes rolling shutter distortion. Then you've got the new BIONZ XR2 processor for 30fps blackout-free bursts, so you don't lose track of your subject because of, well, black screens between shots.</p><p>The A7R VI also borrows Auto subject detection mode from the A1 II, so you don't need to manually change the subject you're photographing — handy for anyone who captures a variety of subject. I loved this on the A1 II as I photograph birds, animals and (occasionally) humans and I can't wait to see how it holds up on the A7R VI.</p><h2 id="a-new-trick-up-its-sleeve">A new trick up its sleeve</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:1645px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="P5su7FzHCBigT75dh2KceZ" name="Sony-A7RVI-2" alt="Sony A7R VI" src="https://cdn.mos.cms.futurecdn.net/P5su7FzHCBigT75dh2KceZ-1920-80.jpg" mos="" align="middle" fullscreen="" width="1645" height="926" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>In addition to all the goodies the Sony A7R VI offers, it also features a new battery pack: the 2,670mAh NP-SA100. Sony says that with this battery pack, the A7R VI is CIPA-rated for 600 shots with the viewfinder or 710 shots with the monitor. Its predecessor, the A7R V, is CIPA-rated for 440 shots, so Sony's made some strides.</p><p>CIPA testing is done under strict conditions and the number is often conservative, so it'll be interesting to see how the battery holds up in real-world use.</p><p>I think it's going to be interesting to see how both the A7R VI and the Canon EOS R6 V perform in practice as, in theory, they both sound great in their own right. I'm obviously not pitting both cameras against one another. That would be unfair as they have different target audiences (and there's quite a bit of a price difference). Needless to say, though, it's an exciting time for photographers and camera enthusiasts. I can't wait to get my hands on both cameras and put them through their paces!</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eJkPyW"></div>                            </div>                            <script src="https://kwizly.com/embed/eJkPyW.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/cameras-photography/mirrorless-cameras/ive-got-two-masters-degrees-and-the-sony-a1-ii-makes-me-feel-stupid-heres-why">I tested the Sony A1 II for 2 weeks — and its autofocus results blew my mind</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-just-tested-the-canon-eos-r5-mark-ii-to-photograph-wildlife-and-this-one-feature-has-changed-how-i-take-photos-forever">I just tested the Canon EOS R5 Mark II to photograph wildlife — and this one feature has changed how I take photos forever</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-wildlife-camera-first-impressions">‘Few wildlife cameras come close to this’: why the OM System OM-1 Mark II is perfect for photographing birds in flight and other animals</a></li></ul><div data-widget-type="multimodelreview" data-model-name="Sony A7R VI,Canon EOS R6 V"></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Don't splurge on the brand new Sony A7R VI just yet — I'd get its awesome predecessor for $900 off right now ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Earlier this week, Sony revealed the latest addition to its extensive lineup of mirrorless cameras: the Sony A7R VI. It succeeds the four-year-old <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a7r-v-review">Sony A7R V</a>, and on paper, it sounds like a dream. It packs a newly designed 66.8MP sensor, making it the highest resolution full-frame camera today. 30fps RAW burst, a next-gen processor, Auto subject detection, a high-res EVF and a 4-axis monitor — it's got it all.</p><p>But that doesn't mean the A7R V is going anywhere (for now). The A7R V is still one of the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless cameras</a> you can buy today, and in light of its successor now being a reality, it's cheaper than ever. That's right, the <a href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" target="_blank" rel="nofollow">A7R V body is currently $900 off at Adorama</a>, making it a little more affordable at $3,298 (rather than its retail price of $4,198).</p><div class="product"><a data-dimension112="e97493bf-fe17-4291-9576-2a2051734fc8" data-action="Deal Block" data-label="A7R V" data-dimension48="A7R V" data-dimension25="$3298" href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" 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:68.93%;"><img id="DBkLKC6xyDRtt2gFTkSHsd" name="Sony A7RV deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/DBkLKC6xyDRtt2gFTkSHsd-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="1034" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony A7R V is an epic full frame hybrid with enough resolution to retire on. Stills are, as you can imagine, beautiful, combined with Sony’s vaunted AF and stabilization. And there’s some chunky video specs, such as 8K/30fps, to boot.<a class="view-deal button" href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" target="_blank" rel="nofollow" data-dimension112="e97493bf-fe17-4291-9576-2a2051734fc8" data-action="Deal Block" data-label="A7R V" data-dimension48="A7R V" data-dimension25="$3298">View Deal</a></p></div><p>When we reviewed the A7R V, we awarded it a 4.5-star rating along with the coveted Editor's Choice Award — and for good reason, too. This camera's got everything you need to create stellar content, from high-res stills to cinematic videos. The A7R V packs a 61MP sensor that enables you to make extreme crops, and powerful autofocus as well as strong high dynamic range performance make it a winner.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/zPiwzpQUcCKS3SjhLJakEh-1920-80.jpg" alt="A black and white photograph of a greyhound lying in a bed looking at the camera" /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/TZwAc7qhAPfbjBFmfUxVkG-1920-80.jpg" alt="A photo of a green flower with a caterpillar on the inside of one petal." /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/koPvtgvkw5b2RzVYwiALV5-1920-80.jpg" alt="A seagull standing on the ground" /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/X5uaBKB2gqC4TD32ftS8S5-1920-80.jpg" alt="Three greyhounds running" /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure></figure><p>The A7R V features up to 8-stops of in-body image stabilization (IBIS) which enables you to shoot at slow shutter speeds, making low-light photography a walk in the park. The colors it captures are nothing short of beautiful, and if you want a powerful hybrid camera, it can shoot 8K/30fps video too.</p><p>What makes the A7R V a sheer delight to use is its 0.64-inch Quad-XGA OLED electronic viewfinder (EVF) with a resolution of 9.44M dots. That means it's bright, extremely sharp, and great for extensive focus checking of those highly detailed 61MP images. Ergonomics are peerless, thanks to the protruding sculpted grip and a body that's heavy enough that it feels sturdy but not <em>too </em>heavy that it feels unwieldy.</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="swGKMYPRqDbWisx7f7Ao2N" name="Sony A7RV" alt="The Sony A7RV against a blue background" src="https://cdn.mos.cms.futurecdn.net/swGKMYPRqDbWisx7f7Ao2N-1920-80.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 Photo Studios)</span></figcaption></figure><p>I'm very excited to get my hands on the new Sony A7R VI because it's got some drool-worthy specs — but so does the older A7R V. I personally <a href="https://www.tomsguide.com/cameras-photography/you-dont-need-to-upgrade-your-camera-just-because-a-new-one-has-come-out-here-are-5-older-cameras-that-still-take-stunning-photos">don't believe everyone needs to get the latest camera as old cameras still take stunning photos</a>, and the A7R V is one of them. The <a href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" target="_blank" rel="nofollow">A7R V body is $900 off at Adorama right now</a> which make it a steal, and as it's a limited time offer, I'd grab this deal with both hands and run.</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/cameras-photography/mirrorless-cameras/sony-a7r-v-review">Sony A7R V review: The ultimate Sony hybrid</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/ive-got-two-masters-degrees-and-the-sony-a1-ii-makes-me-feel-stupid-heres-why">I tested the Sony A1 II for 2 weeks — and its autofocus results blew my mind</a></li><li><a href="https://www.tomsguide.com/cameras-photography/if-i-had-to-start-my-camera-setup-from-scratch-id-pick-these-5-cameras-and-drones-heres-why">If I had to start my camera setup from scratch, I’d pick these 5 cameras and drones — here’s why</a></li></ul><div data-widget-type="multimodelreview" data-model-name="Sony A7R V,Sony A7R VI"></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/mirrorless-cameras/dont-splurge-on-the-brand-new-sony-a7r-vi-just-yet-id-get-its-awesome-predecessor-for-usd900-off-right-now</link>
                                                                            <description>
                            <![CDATA[ Sony dropped the A7R VI earlier this week and in light of that, its predecessor has received a massive price cut. You can save $900 on the A7R V today. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">UAME6CBduqrDjKJZGJs2yA</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/fgN7q2yQR3sZdPkF8wcrZE-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 15 May 2026 10:55:52 +0000</pubDate>                                                                                                                                <updated>Fri, 15 May 2026 14:50:29 +0000</updated>
                                                                                                                                            <category><![CDATA[Mirrorless Cameras]]></category>
                                                    <category><![CDATA[Cameras & Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3-320-70.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&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/fgN7q2yQR3sZdPkF8wcrZE-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A person holding a Sony A7R V mirrorless camera]]></media:description>                                                            <media:text><![CDATA[A person holding a Sony A7R V mirrorless camera]]></media:text>
                                <media:title type="plain"><![CDATA[A person holding a Sony A7R V mirrorless camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/fgN7q2yQR3sZdPkF8wcrZE-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Earlier this week, Sony revealed the latest addition to its extensive lineup of mirrorless cameras: the Sony A7R VI. It succeeds the four-year-old <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a7r-v-review">Sony A7R V</a>, and on paper, it sounds like a dream. It packs a newly designed 66.8MP sensor, making it the highest resolution full-frame camera today. 30fps RAW burst, a next-gen processor, Auto subject detection, a high-res EVF and a 4-axis monitor — it's got it all.</p><p>But that doesn't mean the A7R V is going anywhere (for now). The A7R V is still one of the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless cameras</a> you can buy today, and in light of its successor now being a reality, it's cheaper than ever. That's right, the <a href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" target="_blank" rel="nofollow">A7R V body is currently $900 off at Adorama</a>, making it a little more affordable at $3,298 (rather than its retail price of $4,198).</p><div class="product"><a data-dimension112="e97493bf-fe17-4291-9576-2a2051734fc8" data-action="Deal Block" data-label="A7R V" data-dimension48="A7R V" data-dimension25="$3298" href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" 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:68.93%;"><img id="DBkLKC6xyDRtt2gFTkSHsd" name="Sony A7RV deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/DBkLKC6xyDRtt2gFTkSHsd-1920-80.jpg" mos="" align="middle" fullscreen="" width="1500" height="1034" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony A7R V is an epic full frame hybrid with enough resolution to retire on. Stills are, as you can imagine, beautiful, combined with Sony’s vaunted AF and stabilization. And there’s some chunky video specs, such as 8K/30fps, to boot.<a class="view-deal button" href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" target="_blank" rel="nofollow" data-dimension112="e97493bf-fe17-4291-9576-2a2051734fc8" data-action="Deal Block" data-label="A7R V" data-dimension48="A7R V" data-dimension25="$3298">View Deal</a></p></div><p>When we reviewed the A7R V, we awarded it a 4.5-star rating along with the coveted Editor's Choice Award — and for good reason, too. This camera's got everything you need to create stellar content, from high-res stills to cinematic videos. The A7R V packs a 61MP sensor that enables you to make extreme crops, and powerful autofocus as well as strong high dynamic range performance make it a winner.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/zPiwzpQUcCKS3SjhLJakEh-1920-80.jpg" alt="A black and white photograph of a greyhound lying in a bed looking at the camera" /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/TZwAc7qhAPfbjBFmfUxVkG-1920-80.jpg" alt="A photo of a green flower with a caterpillar on the inside of one petal." /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/koPvtgvkw5b2RzVYwiALV5-1920-80.jpg" alt="A seagull standing on the ground" /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/X5uaBKB2gqC4TD32ftS8S5-1920-80.jpg" alt="Three greyhounds running" /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure></figure><p>The A7R V features up to 8-stops of in-body image stabilization (IBIS) which enables you to shoot at slow shutter speeds, making low-light photography a walk in the park. The colors it captures are nothing short of beautiful, and if you want a powerful hybrid camera, it can shoot 8K/30fps video too.</p><p>What makes the A7R V a sheer delight to use is its 0.64-inch Quad-XGA OLED electronic viewfinder (EVF) with a resolution of 9.44M dots. That means it's bright, extremely sharp, and great for extensive focus checking of those highly detailed 61MP images. Ergonomics are peerless, thanks to the protruding sculpted grip and a body that's heavy enough that it feels sturdy but not <em>too </em>heavy that it feels unwieldy.</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="swGKMYPRqDbWisx7f7Ao2N" name="Sony A7RV" alt="The Sony A7RV against a blue background" src="https://cdn.mos.cms.futurecdn.net/swGKMYPRqDbWisx7f7Ao2N-1920-80.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 Photo Studios)</span></figcaption></figure><p>I'm very excited to get my hands on the new Sony A7R VI because it's got some drool-worthy specs — but so does the older A7R V. I personally <a href="https://www.tomsguide.com/cameras-photography/you-dont-need-to-upgrade-your-camera-just-because-a-new-one-has-come-out-here-are-5-older-cameras-that-still-take-stunning-photos">don't believe everyone needs to get the latest camera as old cameras still take stunning photos</a>, and the A7R V is one of them. The <a href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" target="_blank" rel="nofollow">A7R V body is $900 off at Adorama right now</a> which make it a steal, and as it's a limited time offer, I'd grab this deal with both hands and run.</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/cameras-photography/mirrorless-cameras/sony-a7r-v-review">Sony A7R V review: The ultimate Sony hybrid</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/ive-got-two-masters-degrees-and-the-sony-a1-ii-makes-me-feel-stupid-heres-why">I tested the Sony A1 II for 2 weeks — and its autofocus results blew my mind</a></li><li><a href="https://www.tomsguide.com/cameras-photography/if-i-had-to-start-my-camera-setup-from-scratch-id-pick-these-5-cameras-and-drones-heres-why">If I had to start my camera setup from scratch, I’d pick these 5 cameras and drones — here’s why</a></li></ul><div data-widget-type="multimodelreview" data-model-name="Sony A7R V,Sony A7R VI"></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Still playing a PS4? This simple upgrade changes everything ]]></title>
                                                                                                <dc:content><![CDATA[ <p>A PS5 is a serious investment. Meanwhile, your PS4 is starting to show its age.</p><p>Here's the thing: your PS4's real bottleneck is a spinning hard drive — the same technology you'd find in a budget laptop from a decade ago. Swap it for an SSD and load times drop dramatically. The drive costs less than a single new game and a screwdriver is the only tool you need.</p><p>The catch is you can't just yank one drive and slot in another. You'll need to back up your saves, reinstall the OS fresh on the new drive, then pull everything back. Two USB drives, about an hour, mild inconvenience. Here's exactly how.</p><section class="howto-block">                    <h3>1. Back up your game saves and data</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/6DkbqJRAkCLqHXGdsFQEHg.jpg"                                        alt="ps4"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/6DkbqJRAkCLqHXGdsFQEHg.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>The new SSD will be blank, so you need to save everything from your current hard drive first. You'll need a USB drive with enough space for your game saves and any apps you want to avoid re-downloading.</p><p><strong>Insert the USB drive into your PS4 and head to Settings, System, Back Up and Restore, then select "Back Up PS4."</strong> Choose what to back up — at minimum, select your saved data. You can include applications too, though games themselves are too large and will need reinstalling regardless.</p><p>If you have PlayStation Plus, you can skip the USB drive and back up to cloud storage instead. For trophies, sync them through the Trophies section before you do anything else.</p><p>Once the backup is done, <strong>shut the PS4 down completely and unplug it</strong> before opening the console.</p></p>                </section><section class="howto-block">                    <h3>2. Replace the hard drive with an SSD</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/VK3o26dZDd92LTgsDMVRcU.jpg"                                        alt="2.5 SSD"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/VK3o26dZDd92LTgsDMVRcU.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p><strong>You need a 2.5-inch SATA SSD</strong> — not the slimmer type used in modern PCs and the PS5. Any 2.5-inch model will do, you don't need anything high-end.</p><p>The process varies slightly depending on which PS4 you have, but the principle is the same across all models: <strong>remove a panel, unscrew the drive cage, swap the drive, and reassemble</strong>. </p><p>Once that's done, don't turn it on yet, you need to get the operating system ready first.</p></p>                </section><section class="howto-block">                    <h3>3. Reinstall the PS4 operating system</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/oAVe3cFCt2JWQw2GVpH6ab.jpg"                                        alt=""                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/oAVe3cFCt2JWQw2GVpH6ab.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Joeri Mostmans/Shutterstock)</div></figure>                    <p><p>Your new SSD is blank, so you'll need to install the PS4 operating system from scratch using a second USB drive and a computer.</p><p><strong>Format the USB drive, create a specific folder structure on it, then download the </strong><a href="https://www.playstation.com/en-us/support/hardware/ps4/system-software/#reinstall" target="_blank"><strong>full reinstallation file from Sony's support page</strong></a><strong> and drop it in</strong>. The file name needs to stay exactly as downloaded.</p><p>From there, <strong>boot your PS4 into Safe Mode by holding the power button</strong> until you hear a second beep, <strong>select "Initialize PS4 </strong>(Reinstall System Software)", i<strong>nsert the USB drive,</strong> and let it do its thing. When it's done, the PS4 reboots like it's brand new.</p></p>                </section><section class="howto-block">                    <h3>4. Restore your data and reinstall games</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/4qwE9oxc69wopgE4jcWE8a.jpg"                                        alt="PS4"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/4qwE9oxc69wopgE4jcWE8a.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Getty)</div></figure>                    <p><p><strong>Log in and set up as normal, then restore the backup you made earlier </strong>— either from your USB drive or cloud storage, depending on which route you took.</p><p>Your saves and settings will come back, but <strong>you'll need to reinstall your games</strong>. Once everything is back in place, the difference in loading times should be immediately obvious.</p></p>                </section><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Xp48GX"></div>                            </div>                            <script src="https://kwizly.com/embed/Xp48GX.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-1920-80.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="http://tomsguide.com/gaming/playstation/just-got-a-ps5-heres-7-settings-to-change-first">Just got a PS5? Here's 7 settings to change first</a></li><li><a href="https://www.tomsguide.com/tvs/heres-how-to-stop-your-tv-from-turning-on-your-xbox-or-ps5">Here's how to stop your TV from turning on your Xbox or PS5</a></li><li><a href="https://www.tomsguide.com/gaming/playstation/how-to-turn-on-enhance-image-quality-for-ps4-games-on-ps5-pro">Your PS5 Pro can boost image quality on PS4 games — here's how </a></li></ul><div data-model-name="Sony PlayStation 5 Slim,Microsoft Xbox Series X,Xbox Series S,Nintendo Switch OLED" data-widget-type="comparison"></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/gaming/playstation/your-ps4-is-secretly-holding-itself-back-heres-the-fix</link>
                                                                            <description>
                            <![CDATA[ Learn how a simple hardware swap can make your ageing PS4 feel like a different machine. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">37Nrz6Rt6t5eYGoPyghdA7</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/6XpZp6H95BBHfAWQcopDCG-1920-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 04 May 2026 06:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Playstation]]></category>
                                                    <category><![CDATA[Gaming]]></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/sUwQW8yCGXFHeksAFjqSxZ-320-70.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/6XpZp6H95BBHfAWQcopDCG-1920-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[PS4]]></media:description>                                                            <media:text><![CDATA[PS4]]></media:text>
                                <media:title type="plain"><![CDATA[PS4]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/6XpZp6H95BBHfAWQcopDCG-1920-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>A PS5 is a serious investment. Meanwhile, your PS4 is starting to show its age.</p><p>Here's the thing: your PS4's real bottleneck is a spinning hard drive — the same technology you'd find in a budget laptop from a decade ago. Swap it for an SSD and load times drop dramatically. The drive costs less than a single new game and a screwdriver is the only tool you need.</p><p>The catch is you can't just yank one drive and slot in another. You'll need to back up your saves, reinstall the OS fresh on the new drive, then pull everything back. Two USB drives, about an hour, mild inconvenience. Here's exactly how.</p><section class="howto-block">                    <h3>1. Back up your game saves and data</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/6DkbqJRAkCLqHXGdsFQEHg.jpg"                                        alt="ps4"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/6DkbqJRAkCLqHXGdsFQEHg.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p>The new SSD will be blank, so you need to save everything from your current hard drive first. You'll need a USB drive with enough space for your game saves and any apps you want to avoid re-downloading.</p><p><strong>Insert the USB drive into your PS4 and head to Settings, System, Back Up and Restore, then select "Back Up PS4."</strong> Choose what to back up — at minimum, select your saved data. You can include applications too, though games themselves are too large and will need reinstalling regardless.</p><p>If you have PlayStation Plus, you can skip the USB drive and back up to cloud storage instead. For trophies, sync them through the Trophies section before you do anything else.</p><p>Once the backup is done, <strong>shut the PS4 down completely and unplug it</strong> before opening the console.</p></p>                </section><section class="howto-block">                    <h3>2. Replace the hard drive with an SSD</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/VK3o26dZDd92LTgsDMVRcU.jpg"                                        alt="2.5 SSD"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/VK3o26dZDd92LTgsDMVRcU.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Shutterstock)</div></figure>                    <p><p><strong>You need a 2.5-inch SATA SSD</strong> — not the slimmer type used in modern PCs and the PS5. Any 2.5-inch model will do, you don't need anything high-end.</p><p>The process varies slightly depending on which PS4 you have, but the principle is the same across all models: <strong>remove a panel, unscrew the drive cage, swap the drive, and reassemble</strong>. </p><p>Once that's done, don't turn it on yet, you need to get the operating system ready first.</p></p>                </section><section class="howto-block">                    <h3>3. Reinstall the PS4 operating system</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/oAVe3cFCt2JWQw2GVpH6ab.jpg"                                        alt=""                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/oAVe3cFCt2JWQw2GVpH6ab.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Joeri Mostmans/Shutterstock)</div></figure>                    <p><p>Your new SSD is blank, so you'll need to install the PS4 operating system from scratch using a second USB drive and a computer.</p><p><strong>Format the USB drive, create a specific folder structure on it, then download the </strong><a href="https://www.playstation.com/en-us/support/hardware/ps4/system-software/#reinstall" target="_blank"><strong>full reinstallation file from Sony's support page</strong></a><strong> and drop it in</strong>. The file name needs to stay exactly as downloaded.</p><p>From there, <strong>boot your PS4 into Safe Mode by holding the power button</strong> until you hear a second beep, <strong>select "Initialize PS4 </strong>(Reinstall System Software)", i<strong>nsert the USB drive,</strong> and let it do its thing. When it's done, the PS4 reboots like it's brand new.</p></p>                </section><section class="howto-block">                    <h3>4. Restore your data and reinstall games</h3>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="http://cdn.mos.cms.futurecdn.net/4qwE9oxc69wopgE4jcWE8a.jpg"                                        alt="PS4"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="http://cdn.mos.cms.futurecdn.net/4qwE9oxc69wopgE4jcWE8a.jpg"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Getty)</div></figure>                    <p><p><strong>Log in and set up as normal, then restore the backup you made earlier </strong>— either from your USB drive or cloud storage, depending on which route you took.</p><p>Your saves and settings will come back, but <strong>you'll need to reinstall your games</strong>. Once everything is back in place, the difference in loading times should be immediately obvious.</p></p>                </section><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Xp48GX"></div>                            </div>                            <script src="https://kwizly.com/embed/Xp48GX.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-1920-80.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="http://tomsguide.com/gaming/playstation/just-got-a-ps5-heres-7-settings-to-change-first">Just got a PS5? Here's 7 settings to change first</a></li><li><a href="https://www.tomsguide.com/tvs/heres-how-to-stop-your-tv-from-turning-on-your-xbox-or-ps5">Here's how to stop your TV from turning on your Xbox or PS5</a></li><li><a href="https://www.tomsguide.com/gaming/playstation/how-to-turn-on-enhance-image-quality-for-ps4-games-on-ps5-pro">Your PS5 Pro can boost image quality on PS4 games — here's how </a></li></ul><div data-model-name="Sony PlayStation 5 Slim,Microsoft Xbox Series X,Xbox Series S,Nintendo Switch OLED" data-widget-type="comparison"></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
            </channel>
</rss>